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 @@ -13,6 +13,9 @@
remove from query summaries.
([#3663](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3663))

* Improve SQL parsing to generate query summaries for more T-SQL keywords.
([#3671](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3671))

## 1.14.0-beta.2

Released 2025-Nov-14
Expand Down
3 changes: 3 additions & 0 deletions src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
remove from query summaries.
([#3663](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3663))

* Improve SQL parsing to generate query summaries for more T-SQL keywords.
([#3671](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3671))

* Make additional attributes available on `Activity` start. The full list of
attributes available at time of sampling now conforms with the
[semantic conventions](https://github.com/open-telemetry/semantic-conventions/blob/v1.33.0/docs/database/database-spans.md#span-definition).
Expand Down
80 changes: 75 additions & 5 deletions src/Shared/SqlProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ internal static class SqlProcessor
SqlKeywordInfo.AlterKeyword,
SqlKeywordInfo.DropKeyword,
SqlKeywordInfo.ExecKeyword,
SqlKeywordInfo.ExecuteKeyword,
SqlKeywordInfo.GrantKeyword,
SqlKeywordInfo.DenyKeyword,
SqlKeywordInfo.TruncateKeyword,
SqlKeywordInfo.RevokeKeyword,
SqlKeywordInfo.BulkKeyword,
SqlKeywordInfo.DisableKeyword,
SqlKeywordInfo.EnableKeyword,
SqlKeywordInfo.BackupKeyword,
SqlKeywordInfo.RestoreKeyword,
];

// This is a special case used when handling sub-queries in parentheses.
Expand All @@ -77,17 +87,25 @@ internal static class SqlProcessor
private enum SqlKeyword
{
Unknown,
Backup,
Bulk,
Alter,
Clustered,
Connect,
Create,
Database,
Delete,
Deny,
Disable,
Distinct,
Drop,
Enable,
Exec,
Execute,
Exists,
From,
Function,
Grant,
If,
Index,
Insert,
Expand All @@ -98,12 +116,16 @@ private enum SqlKeyword
Not,
On,
Procedure,
Restore,
Revoke,
Role,
Schema,
Select,
Sequence,
Statistics,
Table,
Trigger,
Truncate,
Unique,
Union,
Update,
Expand Down Expand Up @@ -935,29 +957,41 @@ static SqlKeywordInfo()
// Phase 1: Create all static instances.
// We will compare the SQL we are parsing in lowercase, so we store these in lowercase also.
AlterKeyword = new("alter", SqlKeyword.Alter, Unknown);
BackupKeyword = new("backup", SqlKeyword.Backup, Unknown);
BulkKeyword = new("bulk", SqlKeyword.Bulk, Unknown);
ConnectKeyword = new("connect", SqlKeyword.Connect, Unknown);
CreateKeyword = new("create", SqlKeyword.Create, Unknown);
DatabaseKeyword = new("database", SqlKeyword.Database, DdlKeywords);
DatabaseKeyword = new("database", SqlKeyword.Database, [.. DdlKeywords, SqlKeyword.Backup, SqlKeyword.Restore]);
DeleteKeyword = new("delete", SqlKeyword.Delete, Unknown);
DenyKeyword = new("deny", SqlKeyword.Deny, Unknown);
DisableKeyword = new("disable", SqlKeyword.Disable, Unknown);
DropKeyword = new("drop", SqlKeyword.Drop, Unknown);
EnableKeyword = new("enable", SqlKeyword.Enable, Unknown);
ExecKeyword = new("exec", SqlKeyword.Exec, Unknown);
ExecuteKeyword = new("execute", SqlKeyword.Execute, Unknown);
ExistsKeyword = new("exists", SqlKeyword.Exists);
FromKeyword = new("from", SqlKeyword.From);
FunctionKeyword = new("function", SqlKeyword.Function, DdlKeywords);
GrantKeyword = new("grant", SqlKeyword.Grant, Unknown);
IfKeyword = new("if", SqlKeyword.If);
IndexKeyword = new("index", SqlKeyword.Index, [.. DdlKeywords, SqlKeyword.Unique, SqlKeyword.Clustered, SqlKeyword.NonClustered]);
InsertKeyword = new("insert", SqlKeyword.Insert, Unknown);
InsertKeyword = new("insert", SqlKeyword.Insert, [SqlKeyword.Unknown, SqlKeyword.Bulk]);
IntoKeyword = new("into", SqlKeyword.Into);
JoinKeyword = new("join", SqlKeyword.Join);
LoginKeyword = new("login", SqlKeyword.Login, DdlKeywords);
NotKeyword = new("not", SqlKeyword.Not);
OnKeyword = new("on", SqlKeyword.On);
ProcedureKeyword = new("procedure", SqlKeyword.Procedure, DdlKeywords);
RestoreKeyword = new("restore", SqlKeyword.Restore, Unknown);
RevokeKeyword = new("revoke", SqlKeyword.Revoke, Unknown);
RoleKeyword = new("role", SqlKeyword.Role, DdlKeywords);
SchemaKeyword = new("schema", SqlKeyword.Schema, DdlKeywords);
SelectKeyword = new("select", SqlKeyword.Select, [SqlKeyword.Select, SqlKeyword.Unknown]);
SequenceKeyword = new("sequence", SqlKeyword.Sequence, DdlKeywords);
TableKeyword = new("table", SqlKeyword.Table, DdlKeywords);
TriggerKeyword = new("trigger", SqlKeyword.Trigger, DdlKeywords);
StatisticsKeyword = new("statistics", SqlKeyword.Statistics, [SqlKeyword.Update]);
TableKeyword = new("table", SqlKeyword.Table, [.. DdlKeywords, SqlKeyword.Truncate]);
TriggerKeyword = new("trigger", SqlKeyword.Trigger, [.. DdlKeywords, SqlKeyword.Enable, SqlKeyword.Disable]);
TruncateKeyword = new("truncate", SqlKeyword.Truncate, Unknown);
UnionKeyword = new("union", SqlKeyword.Union);
UnknownKeyword = new(string.Empty, SqlKeyword.Unknown);
UpdateKeyword = new("update", SqlKeyword.Update, Unknown);
Expand Down Expand Up @@ -985,11 +1019,17 @@ static SqlKeywordInfo()

// Phase 3: Wire follow relationships
AlterKeyword.FollowedByKeywords = DdlSubKeywords;
BackupKeyword.FollowedByKeywords = [DatabaseKeyword];
BulkKeyword.FollowedByKeywords = [InsertKeyword];
CreateKeyword.FollowedByKeywords = DdlSubKeywords;
DatabaseKeyword.FollowedByKeywords = [IfKeyword];
DenyKeyword.FollowedByKeywords = [ConnectKeyword];
DisableKeyword.FollowedByKeywords = [TriggerKeyword];
DropKeyword.FollowedByKeywords = DdlSubKeywords;
EnableKeyword.FollowedByKeywords = [TriggerKeyword];
FromKeyword.FollowedByKeywords = [JoinKeyword, UnionKeyword];
FunctionKeyword.FollowedByKeywords = [IfKeyword];
GrantKeyword.FollowedByKeywords = [ConnectKeyword];
IfKeyword.FollowedByKeywords = [NotKeyword, ExistsKeyword];
IndexKeyword.FollowedByKeywords = [OnKeyword, IfKeyword];
InsertKeyword.FollowedByKeywords = [IntoKeyword];
Expand All @@ -998,13 +1038,17 @@ static SqlKeywordInfo()
NotKeyword.FollowedByKeywords = [ExistsKeyword];
OnKeyword.FollowedByKeywords = [JoinKeyword];
ProcedureKeyword.FollowedByKeywords = [IfKeyword];
RestoreKeyword.FollowedByKeywords = [DatabaseKeyword];
RevokeKeyword.FollowedByKeywords = [ConnectKeyword];
RoleKeyword.FollowedByKeywords = [IfKeyword];
SchemaKeyword.FollowedByKeywords = [IfKeyword, UnionKeyword];
SelectKeyword.FollowedByKeywords = [FromKeyword];
SequenceKeyword.FollowedByKeywords = [IfKeyword];
TableKeyword.FollowedByKeywords = [IfKeyword];
TriggerKeyword.FollowedByKeywords = [IfKeyword];
TruncateKeyword.FollowedByKeywords = [TableKeyword];
UnionKeyword.FollowedByKeywords = [SelectKeyword];
UpdateKeyword.FollowedByKeywords = [StatisticsKeyword];
UserKeyword.FollowedByKeywords = [IfKeyword];
ViewKeyword.FollowedByKeywords = [IfKeyword];
}
Expand All @@ -1022,22 +1066,38 @@ private SqlKeywordInfo(

public static SqlKeywordInfo AlterKeyword { get; }

public static SqlKeywordInfo BackupKeyword { get; }

public static SqlKeywordInfo BulkKeyword { get; }

public static SqlKeywordInfo ConnectKeyword { get; }

public static SqlKeywordInfo CreateKeyword { get; }

public static SqlKeywordInfo DatabaseKeyword { get; }

public static SqlKeywordInfo DeleteKeyword { get; }

public static SqlKeywordInfo DenyKeyword { get; }

public static SqlKeywordInfo DisableKeyword { get; }

public static SqlKeywordInfo DropKeyword { get; }

public static SqlKeywordInfo EnableKeyword { get; }

public static SqlKeywordInfo ExecKeyword { get; }

public static SqlKeywordInfo ExecuteKeyword { get; }

public static SqlKeywordInfo ExistsKeyword { get; }

public static SqlKeywordInfo FromKeyword { get; }

public static SqlKeywordInfo FunctionKeyword { get; }

public static SqlKeywordInfo GrantKeyword { get; }

public static SqlKeywordInfo IfKeyword { get; }

public static SqlKeywordInfo IndexKeyword { get; }
Expand All @@ -1056,6 +1116,10 @@ private SqlKeywordInfo(

public static SqlKeywordInfo ProcedureKeyword { get; }

public static SqlKeywordInfo RestoreKeyword { get; }

public static SqlKeywordInfo RevokeKeyword { get; }

public static SqlKeywordInfo RoleKeyword { get; }

public static SqlKeywordInfo SchemaKeyword { get; }
Expand All @@ -1064,10 +1128,14 @@ private SqlKeywordInfo(

public static SqlKeywordInfo SequenceKeyword { get; }

public static SqlKeywordInfo StatisticsKeyword { get; }

public static SqlKeywordInfo TableKeyword { get; }

public static SqlKeywordInfo TriggerKeyword { get; }

public static SqlKeywordInfo TruncateKeyword { get; }

public static SqlKeywordInfo UnionKeyword { get; }

public static SqlKeywordInfo UnknownKeyword { get; }
Expand Down Expand Up @@ -1095,6 +1163,9 @@ private SqlKeywordInfo(
SqlKeyword.Into => state.FirstSummaryKeyword is SqlKeyword.Insert,
SqlKeyword.Join => state.FirstSummaryKeyword is SqlKeyword.Select or SqlKeyword.Join,
SqlKeyword.Login or SqlKeyword.User => false,
SqlKeyword.Statistics => state.FirstSummaryKeyword is SqlKeyword.Update,
SqlKeyword.Trigger => state.FirstSummaryKeyword is SqlKeyword.Create or SqlKeyword.Alter or SqlKeyword.Drop or SqlKeyword.Disable or SqlKeyword.Enable,
SqlKeyword.Truncate => state.FirstSummaryKeyword is SqlKeyword.Table,
Comment thread
martincostello marked this conversation as resolved.
SqlKeyword.Database or
SqlKeyword.Function or
SqlKeyword.Index or
Expand All @@ -1103,7 +1174,6 @@ SqlKeyword.Role or
SqlKeyword.Schema or
SqlKeyword.Sequence or
SqlKeyword.Table or
SqlKeyword.Trigger or
SqlKeyword.View => state.FirstSummaryKeyword is SqlKeyword.Create or SqlKeyword.Alter or SqlKeyword.Drop,
_ => false,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1210,8 +1210,7 @@
}
},
{
"name": "revoke",
"skip": "https://github.com/open-telemetry/opentelemetry-dotnet-contrib/issues/3666",
"name": "revoke_select",
"input": {
"db.system.name": "microsoft.sql_server",
"query": "REVOKE SELECT ON SCHEMA :: Sales TO Vendors;"
Expand All @@ -1220,12 +1219,24 @@
"db.query.text": [
"REVOKE SELECT ON SCHEMA :: Sales TO Vendors;"
],
"db.query.summary": "REVOKE SELECT"
"db.query.summary": "REVOKE"
}
},
{
"name": "grant_select",
"input": {
"db.system.name": "microsoft.sql_server",
"query": "GRANT SELECT ON SCHEMA :: Sales TO Vendors;"
},
"expected": {
"db.query.text": [
"GRANT SELECT ON SCHEMA :: Sales TO Vendors;"
],
"db.query.summary": "GRANT"
}
},
{
"name": "grant",
"skip": "https://github.com/open-telemetry/opentelemetry-dotnet-contrib/issues/3666",
"input": {
"db.system.name": "microsoft.sql_server",
"query": "GRANT EXECUTE ON TestProc TO TesterRole WITH GRANT OPTION;"
Expand All @@ -1234,12 +1245,11 @@
"db.query.text": [
"GRANT EXECUTE ON TestProc TO TesterRole WITH GRANT OPTION;"
],
"db.query.summary": "GRANT EXECUTE"
"db.query.summary": "GRANT"
}
},
{
"name": "deny",
"skip": "https://github.com/open-telemetry/opentelemetry-dotnet-contrib/issues/3666",
"input": {
"db.system.name": "microsoft.sql_server",
"query": "DENY CONNECT SQL TO Annika CASCADE;"
Expand All @@ -1248,12 +1258,11 @@
"db.query.text": [
"DENY CONNECT SQL TO Annika CASCADE;"
],
"db.query.summary": "DENY CONNECT"
"db.query.summary": "DENY"
}
},
{
"name": "truncate_table",
"skip": "https://github.com/open-telemetry/opentelemetry-dotnet-contrib/issues/3666",
"input": {
"db.system.name": "microsoft.sql_server",
"query": "TRUNCATE TABLE HumanResources.JobCandidate;"
Expand All @@ -1264,5 +1273,83 @@
],
"db.query.summary": "TRUNCATE TABLE"
}
},
{
"name": "update_statistics",
"input": {
"db.system.name": "microsoft.sql_server",
"query": "UPDATE STATISTICS Sales.SalesOrderDetail (AK_SalesOrderDetail_rowguid);"
},
"expected": {
"db.query.text": [
"UPDATE STATISTICS Sales.SalesOrderDetail (AK_SalesOrderDetail_rowguid);"
],
"db.query.summary": "UPDATE STATISTICS Sales.SalesOrderDetail"
}
},
{
"name": "backup_database",
"input": {
"db.system.name": "microsoft.sql_server",
"query": "BACKUP DATABASE Sales TO URL = 'https://mystorageaccount.blob.core.windows.net/myfirstcontainer/Sales.bak' WITH STATS = 5;"
},
"expected": {
"db.query.text": [
"BACKUP DATABASE Sales TO URL = ? WITH STATS = ?;"
],
"db.query.summary": "BACKUP DATABASE"
}
},
{
"name": "restore_database",
"input": {
"db.system.name": "microsoft.sql_server",
"query": "RESTORE DATABASE AdventureWorks2022 FROM AdventureWorks2022Backups;"
},
"expected": {
"db.query.text": [
"RESTORE DATABASE AdventureWorks2022 FROM AdventureWorks2022Backups;"
],
"db.query.summary": "RESTORE DATABASE"
Comment on lines +1311 to +1313
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other times it does not.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I got a bit bogged down in exactly how to configure things in the code to get it to capture things or not.

Rather than sink too much time into it (or breaking something) I just figured incremental improvement was best for now.

}
},
{
"name": "bulk_insert",
"input": {
"db.system.name": "microsoft.sql_server",
"query": "BULK INSERT my_data FROM 'C:\\data\\data.csv';"
},
"expected": {
"db.query.text": [
"BULK INSERT my_data FROM ?;"
],
"db.query.summary": "BULK INSERT"
}
},
{
"name": "disable_trigger",
"input": {
"db.system.name": "microsoft.sql_server",
"query": "DISABLE TRIGGER Person.uAddress ON Person.Address;"
},
"expected": {
"db.query.text": [
"DISABLE TRIGGER Person.uAddress ON Person.Address;"
],
"db.query.summary": "DISABLE TRIGGER Person.uAddress"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes db.query.summary contains the target of the operation.

}
},
{
"name": "enable_trigger",
"input": {
"db.system.name": "microsoft.sql_server",
"query": "ENABLE TRIGGER Person.uAddress ON Person.Address;"
},
"expected": {
"db.query.text": [
"ENABLE TRIGGER Person.uAddress ON Person.Address;"
],
"db.query.summary": "ENABLE TRIGGER Person.uAddress"
}
}
]
Loading