55
66#nullable disable
77
8+ using System ;
9+ using System . Threading ;
10+ using System . Threading . Tasks ;
11+ using Microsoft . SqlServer . Management . SqlParser . Common ;
12+ using Microsoft . SqlServer . Management . SqlParser . Parser ;
813using Microsoft . SqlTools . ServiceLayer . LanguageServices ;
914using Microsoft . SqlTools . ServiceLayer . LanguageServices . Completion ;
1015using Microsoft . SqlTools . ServiceLayer . LanguageServices . Contracts ;
@@ -19,6 +24,27 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
1924 /// </summary>
2025 public class LanguageServiceTests
2126 {
27+ private sealed class TestLanguageService : LanguageService
28+ {
29+ internal Func < string , ParseResult , ParseOptions , ParseResult > IncrementalParseOverride { get ; set ; }
30+
31+ internal Func < ThreadStart , Thread > CreateParseThreadOverride { get ; set ; }
32+
33+ internal override ParseResult IncrementalParse ( string sqlText , ParseResult previousParseResult , ParseOptions parseOptions )
34+ {
35+ return this . IncrementalParseOverride != null
36+ ? this . IncrementalParseOverride ( sqlText , previousParseResult , parseOptions )
37+ : base . IncrementalParse ( sqlText , previousParseResult , parseOptions ) ;
38+ }
39+
40+ internal override Thread CreateParseThread ( ThreadStart threadStart )
41+ {
42+ return this . CreateParseThreadOverride != null
43+ ? this . CreateParseThreadOverride ( threadStart )
44+ : base . CreateParseThread ( threadStart ) ;
45+ }
46+ }
47+
2248 /// <summary>
2349 /// Verify that the SQL parser correctly detects errors in text
2450 /// </summary>
@@ -186,5 +212,128 @@ public void GetDefaultCompletionListWithMatchesTest()
186212 CompletionItem [ ] result = AutoCompleteHelper . GetDefaultCompletionItems ( scriptDocumentInfo , false ) ;
187213 Assert . AreEqual ( 1 , result . Length ) ;
188214 }
215+
216+ [ Test ]
217+ public async Task ParseAndBindConnectedPathUsesDedicatedParseThreadAndClearsFailedParseState ( )
218+ {
219+ TestLanguageService service = new TestLanguageService ( ) ;
220+ ConnectedBindingQueue bindingQueue = new ConnectedBindingQueue ( false ) ;
221+ service . BindingQueue = bindingQueue ;
222+
223+ var scriptFile = new ScriptFile ( ) ;
224+ scriptFile . SetFileContents ( "SELECT 1" ) ;
225+
226+ var parseOptions = new ParseOptions (
227+ batchSeparator : LanguageService . DefaultBatchSeperator ,
228+ isQuotedIdentifierSet : true ,
229+ compatibilityLevel : DatabaseCompatibilityLevel . Current ,
230+ transactSqlVersion : TransactSqlVersion . Current ) ;
231+
232+ ScriptParseInfo scriptParseInfo = new ScriptParseInfo
233+ {
234+ IsConnected = true ,
235+ ConnectionKey = "test-connection-key" ,
236+ ParseResult = Parser . IncrementalParse ( "SELECT 1" , null , parseOptions )
237+ } ;
238+
239+ service . AddOrUpdateScriptParseInfo ( scriptFile . ClientUri , scriptParseInfo ) ;
240+
241+ ConnectedBindingContext bindingContext = new ConnectedBindingContext
242+ {
243+ IsConnected = false
244+ } ;
245+
246+ bindingQueue . BindingContextMap . TryAdd ( scriptParseInfo . ConnectionKey , bindingContext ) ;
247+ bindingQueue . BindingContextTasks . TryAdd ( bindingContext , Task . FromResult ( 0 ) ) ;
248+
249+ int callingThreadId = Environment . CurrentManagedThreadId ;
250+ int parserThreadId = callingThreadId ;
251+ bool dedicatedThreadCreated = false ;
252+
253+ service . CreateParseThreadOverride = threadStart =>
254+ {
255+ dedicatedThreadCreated = true ;
256+ return new Thread ( threadStart ) ;
257+ } ;
258+
259+ service . IncrementalParseOverride = ( sqlText , previousParseResult , options ) =>
260+ {
261+ parserThreadId = Environment . CurrentManagedThreadId ;
262+ throw new InvalidOperationException ( "parser fault" ) ;
263+ } ;
264+
265+ try
266+ {
267+ ParseResult parseResult = await service . ParseAndBind ( scriptFile , TestObjects . GetTestConnectionInfo ( ) ) ;
268+
269+ Assert . IsNull ( parseResult ) ;
270+ Assert . IsNull ( scriptParseInfo . ParseResult ) ;
271+ Assert . IsTrue ( dedicatedThreadCreated ) ;
272+ Assert . AreNotEqual ( callingThreadId , parserThreadId ) ;
273+ }
274+ finally
275+ {
276+ bindingQueue . StopQueueProcessor ( 1000 ) ;
277+ bindingQueue . Dispose ( ) ;
278+ }
279+ }
280+
281+ [ Test ]
282+ public async Task ParseAndBindConnectedPathClearsParseStateWhenParserReturnsNull ( )
283+ {
284+ TestLanguageService service = new TestLanguageService ( ) ;
285+ ConnectedBindingQueue bindingQueue = new ConnectedBindingQueue ( false ) ;
286+ service . BindingQueue = bindingQueue ;
287+
288+ var scriptFile = new ScriptFile ( ) ;
289+ scriptFile . SetFileContents ( "SELECT 1" ) ;
290+
291+ var parseOptions = new ParseOptions (
292+ batchSeparator : LanguageService . DefaultBatchSeperator ,
293+ isQuotedIdentifierSet : true ,
294+ compatibilityLevel : DatabaseCompatibilityLevel . Current ,
295+ transactSqlVersion : TransactSqlVersion . Current ) ;
296+
297+ ScriptParseInfo scriptParseInfo = new ScriptParseInfo
298+ {
299+ IsConnected = true ,
300+ ConnectionKey = "test-connection-key" ,
301+ ParseResult = Parser . IncrementalParse ( "SELECT 1" , null , parseOptions )
302+ } ;
303+
304+ service . AddOrUpdateScriptParseInfo ( scriptFile . ClientUri , scriptParseInfo ) ;
305+
306+ ConnectedBindingContext bindingContext = new ConnectedBindingContext
307+ {
308+ IsConnected = false
309+ } ;
310+
311+ bindingQueue . BindingContextMap . TryAdd ( scriptParseInfo . ConnectionKey , bindingContext ) ;
312+ bindingQueue . BindingContextTasks . TryAdd ( bindingContext , Task . FromResult ( 0 ) ) ;
313+
314+ bool dedicatedThreadCreated = false ;
315+
316+ service . CreateParseThreadOverride = threadStart =>
317+ {
318+ dedicatedThreadCreated = true ;
319+ return new Thread ( threadStart ) ;
320+ } ;
321+
322+ service . IncrementalParseOverride = ( sqlText , previousParseResult , options ) => null ;
323+
324+ try
325+ {
326+ ParseResult parseResult = await service . ParseAndBind ( scriptFile , TestObjects . GetTestConnectionInfo ( ) ) ;
327+
328+ Assert . IsNull ( parseResult ) ;
329+ Assert . IsNull ( scriptParseInfo . ParseResult ) ;
330+ Assert . IsTrue ( dedicatedThreadCreated ) ;
331+ }
332+ finally
333+ {
334+ bindingQueue . StopQueueProcessor ( 1000 ) ;
335+ bindingQueue . Dispose ( ) ;
336+ }
337+ }
189338 }
190339}
0 commit comments