Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,37 +34,63 @@ steps:
docker pull mcr.microsoft.com/mssql/server:2022-latest

# Password for the SA user (required)
MSSQL_SA_PW=${{parameters.password }}
MSSQL_SA_PW=${{ parameters.password }}

docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$MSSQL_SA_PW" -p 1433:1433 -p 1434:1434 --name sql1 --hostname sql1 -d mcr.microsoft.com/mssql/server:2022-latest

sleep 5

docker ps -a

# Connect to server and get the version:
counter=1
errstatus=1
while [ $counter -le 20 ] && [ $errstatus = 1 ]
# Connect to the SQL Server container and get its version.
#
# It can take a while for the docker container to start listening and be
# ready for connections, so we will wait for up to 2 minutes, checking every
# 3 seconds.

# Wait 3 seconds between attempts.
delay=3

# Try up to 40 times (2 minutes) to connect.
maxAttempts=40

# Attempt counter.
attempt=1

# Flag to indicate when SQL Server is ready to accept connections.
ready=0

while [ $attempt -le $maxAttempts ]
do
echo Waiting for SQL Server to start...
sleep 3
sqlcmd -S 0.0.0.0 -No -U sa -P $MSSQL_SA_PW -Q "SELECT @@VERSION" 2>$SQLCMD_ERRORS
errstatus=$?
((counter++))
echo "Waiting for SQL Server to start (attempt #$attempt of $maxAttempts)..."

sqlcmd -S 127.0.0.1 -No -U sa -P $MSSQL_SA_PW -Q "SELECT @@VERSION" >> $SQLCMD_ERRORS 2>&1

# If the command was successful, then the SQL Server is ready.
if [ $? -eq 0 ]; then
ready=1
break
fi

# Increment the attempt counter.
((attempt++))

# Wait before trying again.
sleep $delay
done

# Display error if connection failed:
if [ $errstatus = 1 ]
# Is the SQL Server ready?
if [ $ready -eq 0 ]
then
echo Cannot connect to SQL Server, installation aborted
# No, so report the error(s) and exit.
echo Cannot connect to SQL Server; installation aborted; errors were:
cat $SQLCMD_ERRORS
rm -f $SQLCMD_ERRORS
exit $errstatus
else
rm -f $SQLCMD_ERRORS
exit 1
fi

rm -f $SQLCMD_ERRORS

echo "Use sqlcmd to show which IP addresses are being listened on..."
echo 0.0.0.0
sqlcmd -S 0.0.0.0 -No -U sa -P $MSSQL_SA_PW -Q "SELECT @@VERSION" -l 2
Expand All @@ -78,7 +104,7 @@ steps:
sqlcmd -No -U sa -P $MSSQL_SA_PW -Q "SELECT @@VERSION" -l 2

echo "Configuring Dedicated Administer Connections to allow remote connections..."
sqlcmd -S 0.0.0.0 -No -U sa -P $MSSQL_SA_PW -Q "sp_configure 'remote admin connections', 1; RECONFIGURE;"
sqlcmd -S 127.0.0.1 -No -U sa -P $MSSQL_SA_PW -Q "sp_configure 'remote admin connections', 1; RECONFIGURE;"
if [ $? = 1 ]
then
echo "Error configuring DAC for remote access."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ public void SqlParameterProperties(string connection)
const string firstColumnName = @"firstColumn";
const string secondColumnName = @"secondColumn";
const string thirdColumnName = @"thirdColumn";
string inputProcedureName = DataTestUtility.GetUniqueName("InputProc").ToString();
string outputProcedureName = DataTestUtility.GetUniqueName("OutputProc").ToString();
string inputProcedureName = DataTestUtility.GetShortName("InputProc").ToString();
string outputProcedureName = DataTestUtility.GetShortName("OutputProc").ToString();
const int charColumnSize = 100;
const int decimalColumnPrecision = 10;
const int decimalColumnScale = 4;
Expand Down Expand Up @@ -722,7 +722,7 @@ public void TestExecuteReader(string connection)
[ClassData(typeof(AEConnectionStringProvider))]
public async Task TestExecuteReaderAsyncWithLargeQuery(string connectionString)
{
string randomName = DataTestUtility.GetUniqueName(Guid.NewGuid().ToString().Replace("-", ""), false);
string randomName = DataTestUtility.GetShortName(Guid.NewGuid().ToString().Replace("-", ""), false);
if (randomName.Length > 50)
{
randomName = randomName.Substring(0, 50);
Expand Down Expand Up @@ -912,8 +912,8 @@ public void TestEnclaveStoredProceduresWithAndWithoutParameters(string connectio
using SqlCommand sqlCommand = new("", sqlConnection, transaction: null,
columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled);

string procWithoutParams = DataTestUtility.GetUniqueName("EnclaveWithoutParams", withBracket: false);
string procWithParam = DataTestUtility.GetUniqueName("EnclaveWithParams", withBracket: false);
string procWithoutParams = DataTestUtility.GetShortName("EnclaveWithoutParams", withBracket: false);
string procWithParam = DataTestUtility.GetShortName("EnclaveWithParams", withBracket: false);

try
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void TestRoundTripWithCspAndCertStoreProvider()
[MemberData(nameof(TestEncryptDecryptWithCsp_Data))]
public void TestEncryptDecryptWithCsp(string connectionString, string providerName, int providerType)
{
string keyIdentifier = DataTestUtility.GetUniqueNameForSqlServer("CSP");
string keyIdentifier = DataTestUtility.GetLongName("CSP");
CspParameters namedCspParameters = new CspParameters(providerType, providerName, keyIdentifier);
using SQLSetupStrategyCspProvider sqlSetupStrategyCsp = new SQLSetupStrategyCspProvider(namedCspParameters);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public static bool TcpConnectionStringDoesNotUseAadAuth
{
get
{
SqlConnectionStringBuilder builder = new (TCPConnectionString);
SqlConnectionStringBuilder builder = new(TCPConnectionString);
return builder.Authentication == SqlAuthenticationMethod.SqlPassword || builder.Authentication == SqlAuthenticationMethod.NotSpecified;
}
}
Expand Down Expand Up @@ -556,59 +556,176 @@ public static bool DoesHostAddressContainBothIPv4AndIPv6()
}
}

// Generate a new GUID and return the characters from its 1st and 4th
// parts, as shown here:
//
// 7ff01cb8-88c7-11f0-b433-00155d7e531e
// ^^^^^^^^ ^^^^
//
// These 12 characters are concatenated together without any
// separators. These 2 parts typically comprise a timestamp and clock
// sequence, most likely to be unique for tests that generate names in
// quick succession.
private static string GetGuidParts()
{
var guid = Guid.NewGuid().ToString();
// GOTCHA: The slice operator is inclusive of the start index and
// exclusive of the end index!
return guid.Substring(0, 8) + guid.Substring(19, 4);
}

/// <summary>
/// Generate a unique name to use in Sql Server;
/// some providers does not support names (Oracle supports up to 30).
/// Generate a short unique database object name, whose maximum length
/// is 30 characters, with the format:
///
/// <Prefix>_<GuidParts>
///
/// The Prefix will be truncated to satisfy the overall maximum length.
///
/// The GUID parts will be the characters from the 1st and 4th blocks
/// from a traditional string representation, as shown here:
///
/// 7ff01cb8-88c7-11f0-b433-00155d7e531e
/// ^^^^^^^^ ^^^^
///
/// These 2 parts typically comprise a timestamp and clock sequence,
/// most likely to be unique for tests that generate names in quick
/// succession. The 12 characters are concatenated together without any
/// separators.
/// </summary>
/// <param name="prefix">The name length will be no more then (16 + prefix.Length + escapeLeft.Length + escapeRight.Length).</param>
/// <param name="withBracket">Name without brackets.</param>
/// <returns>Unique name by considering the Sql Server naming rules.</returns>
public static string GetUniqueName(string prefix, bool withBracket = true)
{
string escapeLeft = withBracket ? "[" : string.Empty;
string escapeRight = withBracket ? "]" : string.Empty;
string uniqueName = string.Format("{0}{1}_{2}_{3}{4}",
escapeLeft,
prefix,
DateTime.Now.Ticks.ToString("X", CultureInfo.InvariantCulture), // up to 8 characters
Guid.NewGuid().ToString().Substring(0, 6), // take the first 6 characters only
escapeRight);
return uniqueName;
///
/// <param name="prefix">
/// The prefix to use when generating the unique name, truncated to at
/// most 18 characters when withBracket is false, and 16 characters when
/// withBracket is true.
///
/// This should not contain any characters that cannot be used in
/// database object names. See:
///
/// https://learn.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-ver17#rules-for-regular-identifiers
/// </param>
///
/// <param name="withBracket">
/// When true, the entire generated name will be enclosed in square
/// brackets, for example:
///
/// [MyPrefix_7ff01cb811f0]
/// </param>
///
/// <returns>
/// A unique database object name, no more than 30 characters long.
/// </returns>
public static string GetShortName(string prefix, bool withBracket = true)
{
StringBuilder name = new(30);

if (withBracket)
{
name.Append('[');
}

int maxPrefixLength = withBracket ? 16 : 18;
if (prefix.Length > maxPrefixLength)
{
prefix = prefix.Substring(0, maxPrefixLength);
}

name.Append(prefix);
name.Append('_');
name.Append(GetGuidParts());

if (withBracket)
{
name.Append(']');
}

return name.ToString();
}

/// <summary>
/// Uses environment values `UserName` and `MachineName` in addition to the specified `prefix` and current date
/// to generate a unique name to use in Sql Server;
/// SQL Server supports long names (up to 128 characters), add extra info for troubleshooting.
/// Generate a long unique database object name, whose maximum length is
/// 96 characters, with the format:
///
/// <Prefix>_<GuidParts>_<UserName>_<MachineName>
///
/// The Prefix will be truncated to satisfy the overall maximum length.
///
/// The GUID Parts will be the characters from the 1st and 4th blocks
/// from a traditional string representation, as shown here:
///
/// 7ff01cb8-88c7-11f0-b433-00155d7e531e
/// ^^^^^^^^ ^^^^
///
/// These 2 parts typically comprise a timestamp and clock sequence,
/// most likely to be unique for tests that generate names in quick
/// succession. The 12 characters are concatenated together without any
/// separators.
///
/// The UserName and MachineName are obtained from the Environment,
/// and will be truncated to satisfy the maximum overall length.
/// </summary>
/// <param name="prefix">Add the prefix to the generate string.</param>
/// <param name="withBracket">Database name must be pass with brackets by default.</param>
/// <returns>Unique name by considering the Sql Server naming rules, never longer than 96 characters.</returns>
public static string GetUniqueNameForSqlServer(string prefix, bool withBracket = true)
{
string extendedPrefix = string.Format(
"{0}_{1}_{2}@{3}",
prefix,
Environment.UserName,
Environment.MachineName,
DateTime.Now.ToString("yyyy_MM_dd", CultureInfo.InvariantCulture));
string name = GetUniqueName(extendedPrefix, withBracket);

// Truncate to no more than 96 characters.
const int maxLen = 96;
if (name.Length > maxLen)
{
if (withBracket)
{
name = name.Substring(0, maxLen - 1) + ']';
}
else
{
name = name.Substring(0, maxLen);
}
///
/// <param name="prefix">
/// The prefix to use when generating the unique name, truncated to at
/// most 32 characters.
///
/// This should not contain any characters that cannot be used in
/// database object names. See:
///
/// https://learn.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-ver17#rules-for-regular-identifiers
/// </param>
///
/// <param name="withBracket">
/// When true, the entire generated name will be enclosed in square
/// brackets, for example:
///
/// [MyPrefix_7ff01cb811f0_test_user_ci_agent_machine_name]
/// </param>
///
/// <returns>
/// A unique database object name, no more than 96 characters long.
/// </returns>
public static string GetLongName(string prefix, bool withBracket = true)
{
StringBuilder name = new(96);

if (withBracket)
{
name.Append('[');
}

if (prefix.Length > 32)
{
prefix = prefix.Substring(0, 32);
}

return name;
name.Append(prefix);
name.Append('_');
name.Append(GetGuidParts());
name.Append('_');

var suffix =
Environment.UserName + '_' +
Environment.MachineName;

int maxSuffixLength = 96 - name.Length;
if (withBracket)
{
--maxSuffixLength;
}
if (suffix.Length > maxSuffixLength)
{
suffix = suffix.Substring(0, maxSuffixLength);
}

name.Append(suffix);

if (withBracket)
{
name.Append(']');
}

return name.ToString();
}

public static void CreateTable(SqlConnection sqlConnection, string tableName, string createBody)
Expand Down Expand Up @@ -1101,6 +1218,8 @@ protected virtual void OnMatchingEventWritten(EventWrittenEventArgs eventData)

public readonly ref struct XEventScope // : IDisposable
{
private const int MaxXEventsLatencyS = 5;

private readonly SqlConnection _connection;
private readonly bool _useDatabaseSession;

Expand Down Expand Up @@ -1135,6 +1254,8 @@ INNER JOIN sys.dm_xe_sessions AS xe

using (SqlCommand command = new SqlCommand(xEventQuery, _connection))
{
Thread.Sleep(MaxXEventsLatencyS * 1000);

if (_connection.State == ConnectionState.Closed)
{
_connection.Open();
Expand All @@ -1155,9 +1276,9 @@ private void SetupXEvent(string eventSpecification, string targetSpecification)
{eventSpecification}
{targetSpecification}
WITH (
MAX_MEMORY=4096 KB,
MAX_MEMORY=16 MB,
EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,
MAX_DISPATCH_LATENCY=30 SECONDS,
MAX_DISPATCH_LATENCY={MaxXEventsLatencyS} SECONDS,
MAX_EVENT_SIZE=0 KB,
MEMORY_PARTITION_MODE=NONE,
TRACK_CAUSALITY=ON,
Expand Down
Loading
Loading