https://github.com/dzsquared/scriptdom-parse-sample
Sample SQL script parser to extract a call to sp_executesql from a lump of T-SQL code.
https://github.com/dzsquared/scriptdom-parse-sample
Last synced: 7 months ago
JSON representation
Sample SQL script parser to extract a call to sp_executesql from a lump of T-SQL code.
- Host: GitHub
- URL: https://github.com/dzsquared/scriptdom-parse-sample
- Owner: dzsquared
- License: mit
- Created: 2024-06-11T21:30:36.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2024-06-11T21:32:30.000Z (almost 2 years ago)
- Last Synced: 2025-02-13T09:15:32.077Z (over 1 year ago)
- Language: C#
- Size: 9.77 KB
- Stars: 0
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ScriptDOM sample for handling SQL statements with sp_executesql
This sample assumes the input is a T-SQL script that uses sp_executesql once to execute a SQL statement. The SQL statement may contain variables that are passed as parameters to sp_executesql.
Example input T-SQL content:
```sql
declare @p0001 nvarchar(73)
set @p0001 = N'UPDATE session SET ss_last_used = @LastUsed WHERE ss_sequence = @sequence'
declare @p0002 nvarchar(32)
set @p0002 = N'@LastUsed datetime,@sequence int'
declare @P0003 datetime set @P0003 = '20240305 15:53:09.093'
declare @P0004 int
set @P0004 = 9482
exec sp_executesql @p0001, @p0002, @LastUsed = @P0003, @sequence = @P0004
```
(line breaks added for readability)
Using [ScriptDOM](https://learn.microsoft.com/en-us/dotnet/api/microsoft.sqlserver.transactsql.scriptdom?view=sql-transactsql-161) we can parse the T-SQL script and extract different versions of the SQL statement executed by sp_executesql.
> [!TIP]
> ScriptDOM is open source and available on [GitHub](https://github.com/microsoft/sqlscriptdom).
## Option 1: what is the SQL statement passed to sp_executesql?
This returns a string based on the content of the variable passed as the first parameter to sp_executesql.
option 1 should output:
```sql
UPDATE session SET ss_last_used = @LastUsed WHERE ss_sequence = @sequence
```
## Option 2: what is the SQL statement executed?
This parses variable substitution such that we get the T-SQL from option 1 after replacing the variables with their values.
option 2 should output:
```sql
UPDATE session SET ss_last_used = '20240305 15:53:09.093' WHERE ss_sequence = 9482
```
## Try it yourself
> [!NOTE]
> The sample code is written in C# and uses the .NET Core SDK. You can download the .NET Core SDK from [here](https://dotnet.microsoft.com/download).
To run the sample, you can use the following command:
```bash
dotnet run
```
To run the sample without the additional output provided to improve your understanding of the process, you can use the following command:
```bash
dotnet run -c Release
```
### Change the code to dynamically accept input
To change the code to accept input from the command line, you can modify the `Program.cs` file.
Replace these 2 lines:
```cs
string filePath = "sample.sql";
int outputTypeInt = 1;
```
with:
```cs
// get file path from user
Console.WriteLine("Enter SQL file path:");
string? filePath = Console.ReadLine();
while (filePath == null)
{
filePath = Console.ReadLine();
}
// get the output type from the user
Console.WriteLine("Enter output type (1 for initial sp_executesql query, 2 for substituted query):");
string? outputType = Console.ReadLine();
int outputTypeInt = 0;
while (Int32.TryParse(outputType, out outputTypeInt) == false)
{
outputType = Console.ReadLine();
}
```
### What are these visitors?
The `TSqlFragmentVisitor` is a class that allows you to visit each node in the parsed T-SQL script. The `TSqlFragmentVisitor` class is abstract, so you need to create a class that inherits from it and override the methods you want to use.
In this sample, we have three visitors:
- **VariableFinderVisitor**: gives us access to all of the `SetVariableStatement` nodes, eg `SET @P0003 = '20240305 15:53:09.093'`
- **spExecuteVisitor**: gives us access to all of the `ExecuteStatement` nodes and we filter it to when the procedure called is `sp_executesql`, eg `exec sp_executesql @p0001, @p0002, @LastUsed = @P0003, @sequence = @P0004`
- **VariableReferenceVisitor**: gives us access to all of the `VariableReference` nodes, eg `@LastUsed` out of `UPDATE session SET ss_last_used = @LastUsed WHERE ss_sequence = @sequence`
When we create an instance of a visitor and pass it to the script (as in the `Accept` method), the visitor will traverse the script and call the appropriate method for each node it encounters.
## References
https://www.sqlservercentral.com/steps/stairway-to-scriptdom-level-1-an-introduction-to-scriptdom
https://deep.data.blog/2013/04/04/using-the-transactsql-scriptdom-parser-to-get-statement-counts/
https://devblogs.microsoft.com/azure-sql/programmatically-parsing-transact-sql-t-sql-with-the-scriptdom-parser/
## License
This sample is available under the MIT license. For more information, see the [LICENSE](LICENSE) file. No support is implied.