@@ -394,6 +394,12 @@ public void TestPreserveCommentsEnabled_SingleLineLeading()
394394 // With PreserveComments, the comment should appear in output
395395 Assert . IsTrue ( generatedSql . Contains ( "-- This is a leading comment" ) ,
396396 "Leading comment should be preserved when PreserveComments is true" ) ;
397+
398+ // Verify comment appears BEFORE the SELECT keyword (correct positioning)
399+ int commentIndex = generatedSql . IndexOf ( "-- This is a leading comment" ) ;
400+ int selectIndex = generatedSql . IndexOf ( "SELECT" , StringComparison . OrdinalIgnoreCase ) ;
401+ Assert . IsTrue ( commentIndex < selectIndex ,
402+ $ "Leading comment should appear before SELECT. Comment at { commentIndex } , SELECT at { selectIndex } ") ;
397403 }
398404
399405 [ TestMethod ]
@@ -418,6 +424,12 @@ public void TestPreserveCommentsEnabled_SingleLineTrailing()
418424 // With PreserveComments, the trailing comment should appear
419425 Assert . IsTrue ( generatedSql . Contains ( "-- trailing comment" ) ,
420426 "Trailing comment should be preserved when PreserveComments is true" ) ;
427+
428+ // Verify trailing comment appears AFTER the SELECT (correct positioning)
429+ int selectIndex = generatedSql . IndexOf ( "SELECT" , StringComparison . OrdinalIgnoreCase ) ;
430+ int commentIndex = generatedSql . IndexOf ( "-- trailing comment" ) ;
431+ Assert . IsTrue ( commentIndex > selectIndex ,
432+ $ "Trailing comment should appear after SELECT. SELECT at { selectIndex } , comment at { commentIndex } ") ;
421433 }
422434
423435 [ TestMethod ]
@@ -447,6 +459,19 @@ public void TestPreserveCommentsEnabled_MultipleStatements()
447459 "First comment should be preserved" ) ;
448460 Assert . IsTrue ( generatedSql . Contains ( "-- Comment between statements" ) ,
449461 "Comment between statements should be preserved" ) ;
462+
463+ // Verify ordering: first comment -> SELECT 1 -> between comment -> SELECT 2
464+ int firstCommentIndex = generatedSql . IndexOf ( "-- First statement" ) ;
465+ int firstSelectIndex = generatedSql . IndexOf ( "SELECT 1" , StringComparison . OrdinalIgnoreCase ) ;
466+ int betweenCommentIndex = generatedSql . IndexOf ( "-- Comment between statements" ) ;
467+ int secondSelectIndex = generatedSql . IndexOf ( "SELECT 2" , StringComparison . OrdinalIgnoreCase ) ;
468+
469+ Assert . IsTrue ( firstCommentIndex < firstSelectIndex ,
470+ "First comment should appear before first SELECT" ) ;
471+ Assert . IsTrue ( firstSelectIndex < betweenCommentIndex ,
472+ "Between comment should appear after first SELECT" ) ;
473+ Assert . IsTrue ( betweenCommentIndex < secondSelectIndex ,
474+ "Between comment should appear before second SELECT" ) ;
450475 }
451476
452477 [ TestMethod ]
@@ -471,6 +496,12 @@ public void TestPreserveCommentsEnabled_MultiLineComment()
471496 // With PreserveComments, the multi-line comment should appear
472497 Assert . IsTrue ( generatedSql . Contains ( "/* This is a multi-line comment */" ) ,
473498 "Multi-line comment should be preserved when PreserveComments is true" ) ;
499+
500+ // Verify comment appears BEFORE the SELECT keyword (correct positioning)
501+ int commentIndex = generatedSql . IndexOf ( "/* This is a multi-line comment */" ) ;
502+ int selectIndex = generatedSql . IndexOf ( "SELECT" , StringComparison . OrdinalIgnoreCase ) ;
503+ Assert . IsTrue ( commentIndex < selectIndex ,
504+ $ "Multi-line comment should appear before SELECT. Comment at { commentIndex } , SELECT at { selectIndex } ") ;
474505 }
475506
476507 [ TestMethod ]
@@ -498,6 +529,220 @@ public void TestPreserveCommentsEnabled_MultiLineBlockComment()
498529 "Decorative comment pattern should be preserved exactly" ) ;
499530 }
500531
532+ [ TestMethod ]
533+ [ Priority ( 0 ) ]
534+ [ SqlStudioTestCategory ( Category . UnitTest ) ]
535+ public void TestPreserveCommentsEnabled_Subquery ( )
536+ {
537+ // Test comments with subqueries
538+ var sqlWithComments = @"-- Outer query comment
539+ SELECT * FROM (
540+ -- Inner subquery comment
541+ SELECT id, name FROM users
542+ ) AS subq;" ;
543+ var parser = new TSql170Parser ( true ) ;
544+ var fragment = parser . Parse ( new StringReader ( sqlWithComments ) , out var errors ) ;
545+
546+ Assert . AreEqual ( 0 , errors . Count ) ;
547+
548+ var generatorOptions = new SqlScriptGeneratorOptions
549+ {
550+ PreserveComments = true
551+ } ;
552+ var generator = new Sql170ScriptGenerator ( generatorOptions ) ;
553+ generator . GenerateScript ( fragment , out var generatedSql ) ;
554+
555+ // Outer comment should be preserved
556+ Assert . IsTrue ( generatedSql . Contains ( "-- Outer query comment" ) ,
557+ "Outer query comment should be preserved" ) ;
558+
559+ // Verify comment appears BEFORE the SELECT keyword (correct positioning)
560+ int commentIndex = generatedSql . IndexOf ( "-- Outer query comment" ) ;
561+ int selectIndex = generatedSql . IndexOf ( "SELECT" , StringComparison . OrdinalIgnoreCase ) ;
562+ Assert . IsTrue ( commentIndex < selectIndex ,
563+ $ "Outer query comment should appear before SELECT. Comment at { commentIndex } , SELECT at { selectIndex } ") ;
564+ }
565+
566+ [ TestMethod ]
567+ [ Priority ( 0 ) ]
568+ [ SqlStudioTestCategory ( Category . UnitTest ) ]
569+ public void TestPreserveCommentsEnabled_CTE ( )
570+ {
571+ // Test comments with Common Table Expressions
572+ var sqlWithComments = @"-- CTE definition comment
573+ WITH cte AS (
574+ SELECT id FROM users
575+ )
576+ -- Main query comment
577+ SELECT * FROM cte;" ;
578+ var parser = new TSql170Parser ( true ) ;
579+ var fragment = parser . Parse ( new StringReader ( sqlWithComments ) , out var errors ) ;
580+
581+ Assert . AreEqual ( 0 , errors . Count ) ;
582+
583+ var generatorOptions = new SqlScriptGeneratorOptions
584+ {
585+ PreserveComments = true
586+ } ;
587+ var generator = new Sql170ScriptGenerator ( generatorOptions ) ;
588+ generator . GenerateScript ( fragment , out var generatedSql ) ;
589+
590+ // CTE comment should be preserved AND appear before the WITH keyword
591+ Assert . IsTrue ( generatedSql . Contains ( "-- CTE definition comment" ) ,
592+ "CTE definition comment should be preserved" ) ;
593+
594+ // Verify comment appears BEFORE the WITH keyword (correct positioning)
595+ int commentIndex = generatedSql . IndexOf ( "-- CTE definition comment" ) ;
596+ int withIndex = generatedSql . IndexOf ( "WITH" , StringComparison . OrdinalIgnoreCase ) ;
597+ Assert . IsTrue ( commentIndex < withIndex ,
598+ $ "CTE comment should appear before WITH keyword. Comment at { commentIndex } , WITH at { withIndex } ") ;
599+ }
600+
601+ [ TestMethod ]
602+ [ Priority ( 0 ) ]
603+ [ SqlStudioTestCategory ( Category . UnitTest ) ]
604+ public void TestPreserveCommentsEnabled_InsertSelect ( )
605+ {
606+ // Test comments with INSERT...SELECT statements
607+ var sqlWithComments = @"-- Insert with select comment
608+ INSERT INTO target_table (col1, col2)
609+ -- Select portion comment
610+ SELECT a, b FROM source_table;" ;
611+ var parser = new TSql170Parser ( true ) ;
612+ var fragment = parser . Parse ( new StringReader ( sqlWithComments ) , out var errors ) ;
613+
614+ Assert . AreEqual ( 0 , errors . Count ) ;
615+
616+ var generatorOptions = new SqlScriptGeneratorOptions
617+ {
618+ PreserveComments = true
619+ } ;
620+ var generator = new Sql170ScriptGenerator ( generatorOptions ) ;
621+ generator . GenerateScript ( fragment , out var generatedSql ) ;
622+
623+ // Insert comment should be preserved
624+ Assert . IsTrue ( generatedSql . Contains ( "-- Insert with select comment" ) ,
625+ "Insert statement comment should be preserved" ) ;
626+
627+ // Verify comment appears BEFORE the INSERT keyword (correct positioning)
628+ int commentIndex = generatedSql . IndexOf ( "-- Insert with select comment" ) ;
629+ int insertIndex = generatedSql . IndexOf ( "INSERT" , StringComparison . OrdinalIgnoreCase ) ;
630+ Assert . IsTrue ( commentIndex < insertIndex ,
631+ $ "Insert comment should appear before INSERT. Comment at { commentIndex } , INSERT at { insertIndex } ") ;
632+ }
633+
634+ [ TestMethod ]
635+ [ Priority ( 0 ) ]
636+ [ SqlStudioTestCategory ( Category . UnitTest ) ]
637+ public void TestPreserveCommentsEnabled_StoredProcedure ( )
638+ {
639+ // Test comments within stored procedure body
640+ var sqlWithComments = @"-- Procedure header comment
641+ CREATE PROCEDURE TestProc
642+ AS
643+ BEGIN
644+ -- First statement in proc
645+ SELECT 1;
646+ -- Second statement in proc
647+ SELECT 2;
648+ END;" ;
649+ var parser = new TSql170Parser ( true ) ;
650+ var fragment = parser . Parse ( new StringReader ( sqlWithComments ) , out var errors ) ;
651+
652+ Assert . AreEqual ( 0 , errors . Count ) ;
653+
654+ var generatorOptions = new SqlScriptGeneratorOptions
655+ {
656+ PreserveComments = true
657+ } ;
658+ var generator = new Sql170ScriptGenerator ( generatorOptions ) ;
659+ generator . GenerateScript ( fragment , out var generatedSql ) ;
660+
661+ // Procedure header comment should be preserved
662+ Assert . IsTrue ( generatedSql . Contains ( "-- Procedure header comment" ) ,
663+ "Procedure header comment should be preserved" ) ;
664+
665+ // Verify comment appears BEFORE the CREATE keyword (correct positioning)
666+ int commentIndex = generatedSql . IndexOf ( "-- Procedure header comment" ) ;
667+ int createIndex = generatedSql . IndexOf ( "CREATE" , StringComparison . OrdinalIgnoreCase ) ;
668+ Assert . IsTrue ( commentIndex < createIndex ,
669+ $ "Procedure comment should appear before CREATE. Comment at { commentIndex } , CREATE at { createIndex } ") ;
670+ }
671+
672+ [ TestMethod ]
673+ [ Priority ( 0 ) ]
674+ [ SqlStudioTestCategory ( Category . UnitTest ) ]
675+ public void TestPreserveCommentsEnabled_MixedCommentStyles ( )
676+ {
677+ // Test mixing single-line and multi-line comments
678+ var sqlWithComments = @"/* Block comment at start */
679+ -- Single line after block
680+ SELECT 1; /* inline block */ -- trailing single" ;
681+ var parser = new TSql170Parser ( true ) ;
682+ var fragment = parser . Parse ( new StringReader ( sqlWithComments ) , out var errors ) ;
683+
684+ Assert . AreEqual ( 0 , errors . Count ) ;
685+
686+ var generatorOptions = new SqlScriptGeneratorOptions
687+ {
688+ PreserveComments = true
689+ } ;
690+ var generator = new Sql170ScriptGenerator ( generatorOptions ) ;
691+ generator . GenerateScript ( fragment , out var generatedSql ) ;
692+
693+ // Both comment styles should be preserved
694+ Assert . IsTrue ( generatedSql . Contains ( "/* Block comment at start */" ) ,
695+ "Block comment should be preserved" ) ;
696+ Assert . IsTrue ( generatedSql . Contains ( "-- Single line after block" ) ,
697+ "Single line comment should be preserved" ) ;
698+
699+ // Verify ordering: block comment -> single line comment -> SELECT
700+ int blockCommentIndex = generatedSql . IndexOf ( "/* Block comment at start */" ) ;
701+ int singleLineIndex = generatedSql . IndexOf ( "-- Single line after block" ) ;
702+ int selectIndex = generatedSql . IndexOf ( "SELECT" , StringComparison . OrdinalIgnoreCase ) ;
703+
704+ Assert . IsTrue ( blockCommentIndex < singleLineIndex ,
705+ "Block comment should appear before single-line comment" ) ;
706+ Assert . IsTrue ( singleLineIndex < selectIndex ,
707+ "Single-line comment should appear before SELECT" ) ;
708+ }
709+
710+ [ TestMethod ]
711+ [ Priority ( 0 ) ]
712+ [ SqlStudioTestCategory ( Category . UnitTest ) ]
713+ public void TestPreserveCommentsEnabled_CreateTable ( )
714+ {
715+ // Test comments with CREATE TABLE statement
716+ var sqlWithComments = @"-- Table creation comment
717+ CREATE TABLE TestTable (
718+ -- Primary key column
719+ Id INT PRIMARY KEY,
720+ -- Name column
721+ Name NVARCHAR(100)
722+ );" ;
723+ var parser = new TSql170Parser ( true ) ;
724+ var fragment = parser . Parse ( new StringReader ( sqlWithComments ) , out var errors ) ;
725+
726+ Assert . AreEqual ( 0 , errors . Count ) ;
727+
728+ var generatorOptions = new SqlScriptGeneratorOptions
729+ {
730+ PreserveComments = true
731+ } ;
732+ var generator = new Sql170ScriptGenerator ( generatorOptions ) ;
733+ generator . GenerateScript ( fragment , out var generatedSql ) ;
734+
735+ // Table creation comment should be preserved
736+ Assert . IsTrue ( generatedSql . Contains ( "-- Table creation comment" ) ,
737+ "Table creation comment should be preserved" ) ;
738+
739+ // Verify comment appears BEFORE the CREATE keyword (correct positioning)
740+ int commentIndex = generatedSql . IndexOf ( "-- Table creation comment" ) ;
741+ int createIndex = generatedSql . IndexOf ( "CREATE" , StringComparison . OrdinalIgnoreCase ) ;
742+ Assert . IsTrue ( commentIndex < createIndex ,
743+ $ "Table comment should appear before CREATE. Comment at { commentIndex } , CREATE at { createIndex } ") ;
744+ }
745+
501746 #endregion
502747 }
503748}
0 commit comments