Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
89d78ea
#513 Add Trace Event Importer (Managed) plugin to replace ReadTrace.e…
PiJoCoder Apr 27, 2026
b82f54a
#513 Fix TraceEventImporter CPU unit conversion, BulkWriter reliabili…
PiJoCoder Apr 27, 2026
0f6f923
Merge branch 'master' of https://github.com/microsoft/SqlNexus into R…
PiJoCoder Apr 28, 2026
6403503
#513: Fix redundant null-coalescing on non-nullable value types and a…
PiJoCoder Apr 28, 2026
b6edf05
#513: Align EventProcessor with ReadTrace.exe and fix CodeQL warnings
PiJoCoder Apr 29, 2026
d59e011
#513: Extract inner SQL from sp_executesql and preserve named parameters
PiJoCoder Apr 29, 2026
45facae
#513 remove the .TRC importer as it wasn't successful in importing du…
PiJoCoder Apr 30, 2026
a425965
#513 fix(TraceEventImporter): fix Unique Batch/Statement Details repo…
PiJoCoder Apr 30, 2026
0821be7
#513 Normalize currency-prefixed numeric parameters in SqlTextNormalizer
PiJoCoder May 6, 2026
6edb329
Merge branch 'master' of https://github.com/microsoft/SqlNexus into R…
PiJoCoder May 6, 2026
deb70c0
#513 fix(importer): add TraceEventImporter to sqlnexus.csproj and har…
PiJoCoder May 6, 2026
3ff32c3
#513 CodeQL and style fixes across Normalization, Readers, and Proce…
PiJoCoder May 7, 2026
79aa24f
#513 #522 feat/fix: local server time import for TraceEvent and ReadT…
PiJoCoder May 8, 2026
ed18339
fix(#513, #522): Always write local-time flag; rename flag to Importe…
PiJoCoder May 8, 2026
e82d44a
#417 changed the text of the customer XEL importer to include system …
PiJoCoder May 8, 2026
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
23 changes: 19 additions & 4 deletions BulkLoadEx/BulkLoadRowset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,25 @@ public DataRow GetNewRow()
if (null == dataTableBuffer)
{
// Get a DataTable w/no rows that captures the schema of the destination table
SqlDataAdapter schemaAdapter = new SqlDataAdapter("select * from [" + targetTable + "] where 1=0", cn);
dataTableBuffer = new DataTable();
dataTableBuffer.TableName = targetTable;
schemaAdapter.Fill(this.dataTableBuffer);
// Handle schema-qualified names (e.g. "ReadTrace.tblTraceFiles") by bracketing each part separately
string quotedName;
int dotIndex = targetTable.IndexOf('.');
if (dotIndex >= 0)
{
string schema = targetTable.Substring(0, dotIndex);
string table = targetTable.Substring(dotIndex + 1);
quotedName = "[" + schema + "].[" + table + "]";
}
else
{
quotedName = "[" + targetTable + "]";
}
using (SqlDataAdapter schemaAdapter = new SqlDataAdapter("select * from " + quotedName + " where 1=0", cn))
{
dataTableBuffer = new DataTable();
dataTableBuffer.TableName = targetTable;
schemaAdapter.Fill(this.dataTableBuffer);
}
}
return this.dataTableBuffer.NewRow();
}
Expand Down
80 changes: 77 additions & 3 deletions ReadTraceNexusImporter/ReadTraceNexusImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
"tbl_ServerProperties " +
"WHERE PropertyName = 'UTCOffset_in_Hours'";

// Flag name written to the DB to record whether timestamps are in local time or UTC.
// SQLNexus_PostProcessing.sql reads this value to choose the correct time conversion.


// Private members
private ArrayList knownRowsets = new ArrayList(); // List of the rowsets we know how to interpret
Expand Down Expand Up @@ -186,6 +189,72 @@
}
}

/// <summary>
/// Writes a flag recording whether timestamps in the ReadTrace tables are already in local
/// server time (<paramref name="isLocalTime"/> = true, value '1') or in UTC (false, '0').
/// Writes to <c>tbl_ServerProperties</c> when it exists, and also sets a
/// <c>ImportedTraceTimestampsInLocalTime</c> column on <c>tbl_server_times</c> (adding the column
/// first if necessary) as a fallback for environments where <c>tbl_ServerProperties</c>
/// is not present. <c>SQLNexus_PostProcessing.sql</c> reads either location to choose
/// the correct UTC offset conversion when populating <c>StartTime_local</c> /
/// <c>EndTime_local</c>.
/// </summary>
private void WriteLocalTimeFlag(bool isLocalTime)
{
// @flagStr = '1' or '0' for VARCHAR columns (tbl_ServerProperties, tblMiscInfo).
// @flagBit = 1 or 0 for the BIT column on tbl_server_times.
// sp_executesql's own @val parameter is fed from the outer @flagBit so the BIT
// update is also fully parameterised with no runtime concatenation.
const string sql =
// --- tbl_ServerProperties (primary) ---
"IF OBJECT_ID('dbo.tbl_ServerProperties') IS NOT NULL " +
"BEGIN " +
" IF EXISTS (SELECT 1 FROM dbo.tbl_ServerProperties WHERE PropertyName = 'ImportedTraceTimestampsInLocalTime') " +
" UPDATE dbo.tbl_ServerProperties SET PropertyValue = @flagStr WHERE PropertyName = 'ImportedTraceTimestampsInLocalTime'; " +
" ELSE " +
" INSERT INTO dbo.tbl_ServerProperties (PropertyName, PropertyValue) VALUES ('ImportedTraceTimestampsInLocalTime', @flagStr); " +
"END " +
// --- tbl_server_times (fallback) ---
// NOTE: ALTER TABLE and UPDATE must be in different batches; sp_executesql is
// used here so the UPDATE is compiled only after the column is already visible.
// The outer @flagBit parameter is forwarded into sp_executesql's own @val.
"IF OBJECT_ID('dbo.tbl_server_times') IS NOT NULL " +
"BEGIN " +
" IF COL_LENGTH('dbo.tbl_server_times', 'ImportedTraceTimestampsInLocalTime') IS NULL " +
" ALTER TABLE dbo.tbl_server_times ADD [ImportedTraceTimestampsInLocalTime] BIT NULL; " +
" IF COL_LENGTH('dbo.tbl_server_times', 'ImportedTraceTimestampsInLocalTime') IS NOT NULL " +
" EXEC sp_executesql N'UPDATE dbo.tbl_server_times SET [ImportedTraceTimestampsInLocalTime] = @val', N'@val BIT', @val = @flagBit; " +
"END " +
// --- ReadTrace.tblMiscInfo (guaranteed fallback) ---
"IF OBJECT_ID('ReadTrace.tblMiscInfo') IS NOT NULL " +
"BEGIN " +
" IF EXISTS (SELECT 1 FROM ReadTrace.tblMiscInfo WHERE Attribute = 'ImportedTraceTimestampsInLocalTime') " +
" UPDATE ReadTrace.tblMiscInfo SET Value = @flagStr WHERE Attribute = 'ImportedTraceTimestampsInLocalTime'; " +
" ELSE " +
" INSERT INTO ReadTrace.tblMiscInfo (Attribute, Value) VALUES ('ImportedTraceTimestampsInLocalTime', @flagStr); " +
"END";

using (SqlConnection cn = new SqlConnection(connStr))
{
try
{
cn.Open();
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
cmd.CommandTimeout = 0;
cmd.Parameters.Add("@flagStr", SqlDbType.VarChar, 1).Value = isLocalTime ? "1" : "0";
cmd.Parameters.Add("@flagBit", SqlDbType.Bit).Value = isLocalTime;
cmd.ExecuteNonQuery();
}
Util.Logger.LogMessage("ReadTraceNexusImporter: Wrote 'ImportedTraceTimestampsInLocalTime'=" + (isLocalTime ? "1" : "0") + " to tbl_ServerProperties, tbl_server_times, and/or ReadTrace.tblMiscInfo.");
}
catch (Exception e)
{
Util.Logger.LogMessage("ReadTraceNexusImporter: Could not write local time flag: " + e.Message);
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.
Comment on lines +251 to +254
}
}

private decimal GetLocalServerTimeOffset()
{
using (SqlConnection cn = new SqlConnection(connStr))
Expand Down Expand Up @@ -292,7 +361,7 @@

/// <summary>Cancel an in-progress load</summary>
/// <remarks>Called by host to ask in importer abort an in-progress load. Can return before abort is complete;
/// the host will wait until <c>DoImport()</c> returns.</remarks>
/// the host will wait until DoImport()</c> returns.</remarks>
public void Cancel()
{
Cancelled = true;
Expand All @@ -312,15 +381,15 @@
State = ImportState.Idle;
}

/// <summary>True if the import has been asked to cancel an in-progress load. Set by the <c>Cancel</c> method.</summary>
/// <summary>True if the import has been asked to cancel an in-progress load. Set by the Cancel</c> method.</summary>
public bool Cancelled
{
get { return canceled; }
set { canceled = value; }
}

/// <summary>Start import</summary>
/// <remarks><c>Initialize()</c> will be called prior to <c>DoImport()</c></remarks>
/// <remarks>Initialize()</c> will be called prior to DoImport()</c></remarks>
/// <returns>true if import succeeds, false otherwise</returns>
public bool DoImport()
{
Expand Down Expand Up @@ -451,7 +520,12 @@
State = ImportState.Idle;

if (0 == processReadTrace.ExitCode)
{
// Always write the flag ('1' = already local time, '0' = UTC) so that
// SQLNexus_PostProcessing.sql always has an explicit value to act on.
WriteLocalTimeFlag((bool)this.options[OPTION_USE_LOCAL_SERVER_TIME]);
return true;
}
else
return false;
}
Expand Down
Loading
Loading