@@ -117,7 +117,7 @@ public static bool TcpConnectionStringDoesNotUseAadAuth
117117 {
118118 get
119119 {
120- SqlConnectionStringBuilder builder = new ( TCPConnectionString ) ;
120+ SqlConnectionStringBuilder builder = new ( TCPConnectionString ) ;
121121 return builder . Authentication == SqlAuthenticationMethod . SqlPassword || builder . Authentication == SqlAuthenticationMethod . NotSpecified ;
122122 }
123123 }
@@ -546,59 +546,176 @@ public static bool DoesHostAddressContainBothIPv4AndIPv6()
546546 }
547547 }
548548
549+ // Generate a new GUID and return the characters from its 1st and 4th
550+ // parts, as shown here:
551+ //
552+ // 7ff01cb8-88c7-11f0-b433-00155d7e531e
553+ // ^^^^^^^^ ^^^^
554+ //
555+ // These 12 characters are concatenated together without any
556+ // separators. These 2 parts typically comprise a timestamp and clock
557+ // sequence, most likely to be unique for tests that generate names in
558+ // quick succession.
559+ private static string GetGuidParts ( )
560+ {
561+ var guid = Guid . NewGuid ( ) . ToString ( ) ;
562+ // GOTCHA: The slice operator is inclusive of the start index and
563+ // exclusive of the end index!
564+ return guid . Substring ( 0 , 8 ) + guid . Substring ( 19 , 4 ) ;
565+ }
566+
549567 /// <summary>
550- /// Generate a unique name to use in Sql Server;
551- /// some providers does not support names (Oracle supports up to 30).
568+ /// Generate a short unique database object name, whose maximum length
569+ /// is 30 characters, with the format:
570+ ///
571+ /// <Prefix>_<GuidParts>
572+ ///
573+ /// The Prefix will be truncated to satisfy the overall maximum length.
574+ ///
575+ /// The GUID parts will be the characters from the 1st and 4th blocks
576+ /// from a traditional string representation, as shown here:
577+ ///
578+ /// 7ff01cb8-88c7-11f0-b433-00155d7e531e
579+ /// ^^^^^^^^ ^^^^
580+ ///
581+ /// These 2 parts typically comprise a timestamp and clock sequence,
582+ /// most likely to be unique for tests that generate names in quick
583+ /// succession. The 12 characters are concatenated together without any
584+ /// separators.
552585 /// </summary>
553- /// <param name="prefix">The name length will be no more then (16 + prefix.Length + escapeLeft.Length + escapeRight.Length).</param>
554- /// <param name="withBracket">Name without brackets.</param>
555- /// <returns>Unique name by considering the Sql Server naming rules.</returns>
556- public static string GetUniqueName ( string prefix , bool withBracket = true )
557- {
558- string escapeLeft = withBracket ? "[" : string . Empty ;
559- string escapeRight = withBracket ? "]" : string . Empty ;
560- string uniqueName = string . Format ( "{0}{1}_{2}_{3}{4}" ,
561- escapeLeft ,
562- prefix ,
563- DateTime . Now . Ticks . ToString ( "X" , CultureInfo . InvariantCulture ) , // up to 8 characters
564- Guid . NewGuid ( ) . ToString ( ) . Substring ( 0 , 6 ) , // take the first 6 characters only
565- escapeRight ) ;
566- return uniqueName ;
586+ ///
587+ /// <param name="prefix">
588+ /// The prefix to use when generating the unique name, truncated to at
589+ /// most 18 characters when withBracket is false, and 16 characters when
590+ /// withBracket is true.
591+ ///
592+ /// This should not contain any characters that cannot be used in
593+ /// database object names. See:
594+ ///
595+ /// https://learn.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-ver17#rules-for-regular-identifiers
596+ /// </param>
597+ ///
598+ /// <param name="withBracket">
599+ /// When true, the entire generated name will be enclosed in square
600+ /// brackets, for example:
601+ ///
602+ /// [MyPrefix_7ff01cb811f0]
603+ /// </param>
604+ ///
605+ /// <returns>
606+ /// A unique database object name, no more than 30 characters long.
607+ /// </returns>
608+ public static string GetShortName ( string prefix , bool withBracket = true )
609+ {
610+ StringBuilder name = new ( 30 ) ;
611+
612+ if ( withBracket )
613+ {
614+ name . Append ( '[' ) ;
615+ }
616+
617+ int maxPrefixLength = withBracket ? 16 : 18 ;
618+ if ( prefix . Length > maxPrefixLength )
619+ {
620+ prefix = prefix . Substring ( 0 , maxPrefixLength ) ;
621+ }
622+
623+ name . Append ( prefix ) ;
624+ name . Append ( '_' ) ;
625+ name . Append ( GetGuidParts ( ) ) ;
626+
627+ if ( withBracket )
628+ {
629+ name . Append ( ']' ) ;
630+ }
631+
632+ return name . ToString ( ) ;
567633 }
568634
569635 /// <summary>
570- /// Uses environment values `UserName` and `MachineName` in addition to the specified `prefix` and current date
571- /// to generate a unique name to use in Sql Server;
572- /// SQL Server supports long names (up to 128 characters), add extra info for troubleshooting.
636+ /// Generate a long unique database object name, whose maximum length is
637+ /// 96 characters, with the format:
638+ ///
639+ /// <Prefix>_<GuidParts>_<UserName>_<MachineName>
640+ ///
641+ /// The Prefix will be truncated to satisfy the overall maximum length.
642+ ///
643+ /// The GUID Parts will be the characters from the 1st and 4th blocks
644+ /// from a traditional string representation, as shown here:
645+ ///
646+ /// 7ff01cb8-88c7-11f0-b433-00155d7e531e
647+ /// ^^^^^^^^ ^^^^
648+ ///
649+ /// These 2 parts typically comprise a timestamp and clock sequence,
650+ /// most likely to be unique for tests that generate names in quick
651+ /// succession. The 12 characters are concatenated together without any
652+ /// separators.
653+ ///
654+ /// The UserName and MachineName are obtained from the Environment,
655+ /// and will be truncated to satisfy the maximum overall length.
573656 /// </summary>
574- /// <param name="prefix">Add the prefix to the generate string.</param>
575- /// <param name="withBracket">Database name must be pass with brackets by default.</param>
576- /// <returns>Unique name by considering the Sql Server naming rules, never longer than 96 characters.</returns>
577- public static string GetUniqueNameForSqlServer ( string prefix , bool withBracket = true )
578- {
579- string extendedPrefix = string . Format (
580- "{0}_{1}_{2}@{3}" ,
581- prefix ,
582- Environment . UserName ,
583- Environment . MachineName ,
584- DateTime . Now . ToString ( "yyyy_MM_dd" , CultureInfo . InvariantCulture ) ) ;
585- string name = GetUniqueName ( extendedPrefix , withBracket ) ;
586-
587- // Truncate to no more than 96 characters.
588- const int maxLen = 96 ;
589- if ( name . Length > maxLen )
590- {
591- if ( withBracket )
592- {
593- name = name . Substring ( 0 , maxLen - 1 ) + ']' ;
594- }
595- else
596- {
597- name = name . Substring ( 0 , maxLen ) ;
598- }
657+ ///
658+ /// <param name="prefix">
659+ /// The prefix to use when generating the unique name, truncated to at
660+ /// most 32 characters.
661+ ///
662+ /// This should not contain any characters that cannot be used in
663+ /// database object names. See:
664+ ///
665+ /// https://learn.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-ver17#rules-for-regular-identifiers
666+ /// </param>
667+ ///
668+ /// <param name="withBracket">
669+ /// When true, the entire generated name will be enclosed in square
670+ /// brackets, for example:
671+ ///
672+ /// [MyPrefix_7ff01cb811f0_test_user_ci_agent_machine_name]
673+ /// </param>
674+ ///
675+ /// <returns>
676+ /// A unique database object name, no more than 96 characters long.
677+ /// </returns>
678+ public static string GetLongName ( string prefix , bool withBracket = true )
679+ {
680+ StringBuilder name = new ( 96 ) ;
681+
682+ if ( withBracket )
683+ {
684+ name . Append ( '[' ) ;
685+ }
686+
687+ if ( prefix . Length > 32 )
688+ {
689+ prefix = prefix . Substring ( 0 , 32 ) ;
690+ }
691+
692+ name . Append ( prefix ) ;
693+ name . Append ( '_' ) ;
694+ name . Append ( GetGuidParts ( ) ) ;
695+ name . Append ( '_' ) ;
696+
697+ var suffix =
698+ Environment . UserName + '_' +
699+ Environment . MachineName ;
700+
701+ int maxSuffixLength = 96 - name . Length ;
702+ if ( withBracket )
703+ {
704+ -- maxSuffixLength ;
705+ }
706+ if ( suffix . Length > maxSuffixLength )
707+ {
708+ suffix = suffix . Substring ( 0 , maxSuffixLength ) ;
709+ }
710+
711+ name . Append ( suffix ) ;
712+
713+ if ( withBracket )
714+ {
715+ name . Append ( ']' ) ;
599716 }
600717
601- return name ;
718+ return name . ToString ( ) ;
602719 }
603720
604721 public static void DropTable ( SqlConnection sqlConnection , string tableName )
0 commit comments