1
0
Форкнуть 0

Added New samples for C# Language extension (#55)

Signed-off-by: Ramanath Nayak <ramnayak@microsoft.com>
This commit is contained in:
Ramanathnayak 2024-09-04 23:22:53 +05:30 коммит произвёл GitHub
Родитель e55508b601
Коммит 1776c22805
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
8 изменённых файлов: 373 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,76 @@
//*********************************************************************
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//
// @File: LoopBackConnectionADONET.cs
//
// Purpose:
// Sample code for loopback connection using ADO.NET driver with comes with the SQLCLIENT implementation.
// This will help in running any custom query against the same SQL server engine where the Language extension is running.
//
//*********************************************************************
using Microsoft.Data.Analysis;
using Microsoft.Data.SqlClient;
using Microsoft.SqlServer.CSharpExtension.SDK;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
namespace UserExecutor
{
/// <summary>
/// This class extends the AbstractSqlServerExtensionExecutor
/// This can be used to run custom SQL queries in the Language extension Environment
/// </summary>
public class LoopBackConnectionADONET: AbstractSqlServerExtensionExecutor
{
/// <summary>
/// This method overrides the Execute method from AbstractSqlServerExtensionExecutor.
/// </summary>
public override DataFrame Execute(DataFrame input, Dictionary<string, dynamic> sqlParams)
{
// Connection string to the server..
// This is a standard connection string.
// Example:
//string connectionstring = "Data Source=<ServerName>;User Id=<<UserID>>;Password=<<Credentials>>;Initial Catalog=<<Database>>;Trusted_Connection=True;Encrypt=False;";
//Connection string is passed as a parm from the execution.
string connectionstring = sqlParams["@connectionString"];
// Create empty output DataFrame with One column
//
DataFrame output = new DataFrame( new StringDataFrameColumn("text", 0));
using (SqlConnection connection = new SqlConnection(connectionstring))
{
connection.Open();
// The SQL command that you need to execute on the SQL server.
// This can be passed as a parameter as well into this method if you want to make it more dynamic.
//String sql = "SELECT field1, field2 FROM table1";
//
String sql = sqlParams["@query"];
//
using (SqlCommand command = new SqlCommand(sql, connection))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
String outstring = "{0} {1}", reader.GetString(0), reader.GetString(1);
Console.WriteLine(outstring);
output.append(outstring);
}
}
}
// Modify the parameters
//
sqlParams["@rowsCount"] = output.Rows.Count;
sqlParams["@Status"] = "Success!";
}
return output;
}
}
}

Просмотреть файл

@ -0,0 +1,72 @@
//*********************************************************************
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//
// @File: LoopBackConnectionOLEDB.cs
//
// Purpose:
// Connect to SQL server using the loopback connection in a Language extension environment.
//
//*********************************************************************
using Microsoft.Data.Analysis;
using Microsoft.Data.SqlClient;
using Microsoft.SqlServer.CSharpExtension.SDK;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Data.OleDb;
namespace UserExecutor
{
/// <summary>
/// This class extends the AbstractSqlServerExtensionExecutor.
/// This class can be used to execute custom SQL queries within the Language Extension environment.
/// </summary>
public class LoopBackConnectionOLEDB : AbstractSqlServerExtensionExecutor
{
/// <summary>
/// This method overrides the Execute method from AbstractSqlServerExtensionExecutor.
/// </summary>
public override DataFrame Execute(DataFrame input, Dictionary<string, dynamic> sqlParams)
{
// Ole DB implementation
// Connection string to the server..
// This is a standard connection string.
// Example:
//string connectionstring = "Data Source=<ServerName>;User Id=<<UserID>>;Password=<<Credentials>>;Initial Catalog=<<Database>>;Trusted_Connection=True;Encrypt=False;";
string connectionstring = sqlParams["@connectionString"];
// Create empty output DataFrame with One column
//
DataFrame output = new DataFrame(new StringDataFrameColumn("text", 0));
using (OleDbConnection connection = new OleDbConnection(connectionstring))
{
connection.Open();
// The SQL command that you need to execute on the SQL server.
// This can be passed as a parameter as well into this method if you want to make it more dynamic.
String sql = sqlParams["@query"];
using (OleDbCommand command = new OleDbCommand(sql, connection))
{
using (OleDbDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
String outstring = "{0} {1}", reader.GetString(0), reader.GetString(1);
Console.WriteLine(outstring);
output.append(outstring);
}
}
}
}
// Modify the parameters
//
sqlParams["@rowsCount"] = output.Rows.Count;
sqlParams["@Status"] = "Success!";
return output;
}
}
}

Просмотреть файл

@ -0,0 +1,13 @@
# Loopback Connection
This example demonstrates how to establish a loopback connection to SQL server. A loopback connection will allow your C# language extension to run any custom query against SQL Server.
## Prerequisites
* [LoopBackConnectionADONET.cs](LoopBackConnectionADONET.cs). This contains the ADO.NET driver implementation sample for Loopback connection.
* [LoopBackCOnnectionOLEDB.cs](LoopBackConnectionOLEDB.cs). This contains the OLEDB driver implementation sample for Loopback Connection.
## Implementation Steps
1) Download/clone the this folder.
2) Depending on what driver you would like to use, edit that particual component.
3) Follow the standard method to outlined in documentation to moved the language Extension DLL code into respective folders.
4) Execute the code through a stored procedure call as outlined in the regex sample.
5) Once your run it you should be able to see the console output in SSMS.

Просмотреть файл

@ -0,0 +1,83 @@
//*********************************************************************
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//
// @File: CallSubModule.cs
//
// Purpose:
// Sample C# code which can dynamically load a class in the SQL Server C# Language Extension.
// This can be used to wrap around different functionality to be extended on top of SQL server.
//*********************************************************************
using Microsoft.Data.Analysis;
using Microsoft.SqlServer.CSharpExtension.SDK;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
namespace UserExecutor
{
/// <summary>
/// This class extends the AbstractSqlServerExtensionExecutor.
/// This can be used to call any custom DLL to further extend the functionality of the C# Language Extension.
/// </summary>
public class CallSubModule: AbstractSqlServerExtensionExecutor
{
/// <summary>
/// This method overrides the Execute method from AbstractSqlServerExtensionExecutor.
/// </summary>
public override DataFrame Execute(DataFrame input, Dictionary<string, dynamic> sqlParams)
{
// The following example demonstrates how to call any C# program/method from an external DLL.
// Access needs to be granted to respective folder before you execute these commands.
// To grant access, run the following commands in a Windows command prompt.
// Note: The folder name may change in your environment. So please specify the right folder name.
// Command1:
// icacls "C:\Program Files\Microsoft SQL Server\MSSQL16.SQLSERVER2022\MSSQL\ExternalLibraries\6\65537\1" /grant "SQLRUsergroupSQLSERVER2022":(OI)(CI)RX /T
// Change the server name according to your naming convension .
// Command2:
// icacls "C:\Program Files\Microsoft SQL Server\MSSQL16.SQLSERVER2022\MSSQL\ExternalLibraries\6\65537\1" /grant *S-1-15-2-1:(OI)(CI)RX /T
// After executing above commands, SQL server will have access to local folder which has been specified in the commands.
// All these commands are available in OneTimeSetupCommands.cmd file in this solution as well
// The required DLL needs to be stored in the location where SQL server has access.
// These DLLs are dynamically loaded during runtime and any method inside of these DLLs can be executed on the fly.
// Below is an example of how we can do that with a sample Hello World Program.
// Template: var DLL = Assembly.LoadFile(@"<Path to DLL>");
//Example
//Object DLL = Assembly.LoadFile(@"C:\Program Files\Microsoft SQL Server\MSSQL16.SQLSERVER2022\MSSQL\ExternalLibraries\6\65537\1\SubModule.dll");
Object DLL = sqlParams["@dllLocation"];
//Template : Type type = DLL.GetType("<Class Name >");
//Example Type type = DLL.GetType("UserExecutor.HelloWorld");
Type type = DLL.GetType(sqlParams["@className"]);
//Template : MethodInfo mi = type.GetMethod("<method name>");
//Example MethodInfo mi = type.GetMethod("printConsole");
MethodInfo mi = type.GetMethod(sqlParams["@methodName"]);
// Create empty output DataFrame with One column
//
DataFrame output = new DataFrame(new StringDataFrameColumn("text", 0));
if (mi != null)
{
object result = null;
ParameterInfo[] parameters = mi.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
if (parameters.Length == 0)
{
result = mi.Invoke(classInstance, null);
output.append("Method invoked Successfully");
}
}
// Modify the parameters
//
sqlParams["@rowsCount"] = output.Rows.Count;
sqlParams["@Status"] = "Success!";
return output;
}
}
}

Просмотреть файл

@ -0,0 +1,66 @@
# SubModule Call
This folder contains an implementation which will help in calling any functionality which has been embedded inside an external DLL. Imagine that you already have a DLL which has a lot of functionality built into it and you would like to reuse all those features in SQL Server Language Extension. This submodule example enables you to reuse your existing code. To represent the custom build, we have created a simple project, which has been placed in SubModuleProject folder. This project just has a console out statement.
## Prerequisites
* SubModuleProject\SubModule\Program.cs: This is a sample custom code with simple console log.
* SubModuleProject\SubModule\SubModule.csproj: This is the .NET project file which encapsulated the custom code.
* SubModuleProject\SubModule\SubModule.sln : Solution file for the custom project.
* CallSubModule.cs : This is the actual module which has the implementation to call a external DLL. Modify this code to point to the right folder where the external DLLs are stored.
## Implementation Steps
1. Download/clone the SubModuleProject . Lets assume that you have cloned the repo in a folder named "C:\Dev\language-extensions"
```bash
git clone https://github.com/microsoft/sql-server-language-extensions.git
```
2. Compile the project SubModule.sln. This will generete a DLL file named SubModule.dll
```bash
dotnet build C:\Dev\language-extensions\language-extensions\dotnet-core-CSharp\sample\SubModuleCall\SubModuleProject\SubModule\SubModule.csproj
```
3. Move this DLL file to the respective folder of the SQL Server Language Extension.
```bash
copy "C:\Dev\language-extensions\language-extensions\dotnet-core-CSharp\sample\SubModuleCall\SubModuleProject\SubModule\bin\Debug\net8.0\SubModule.dll" "C:\Program Files\Microsoft SQL Server\MSSQL16.SQLSERVER2022\MSSQL\ExternalLibraries\6\65537\1"
```
4. Grant access to the DLL once it is moved. Below are the commands to grant access. Run these in a command prompt window.
```bash
icacls "C:\Program Files\Microsoft SQL Server\MSSQL16.SQLSERVER2022\MSSQL\ExternalLibraries\6\65537\1" /grant "SQLRUsergroupSQLSERVER2022":(OI)(CI)RX /T
icacls "C:\Program Files\Microsoft SQL Server\MSSQL16.SQLSERVER2022\MSSQL\ExternalLibraries\6\65537\1" /grant *S-1-15-2-1:(OI)(CI)RX /T
```
5) Call [CallSubModule.cs](./CallSubModule.cs) using the below sample code .
```sql
declare @rowsCount int
declare @dllLocation varchar(200)
declare @className varchar(30)
declare @methodName varchar(30)
declare @Status varchar(30)
set @dllLocation = N'C:\Program Files\Microsoft SQL Server\MSSQL16.SQLSERVER2022\MSSQL\ExternalLibraries\6\65537\1\SubModule.dll'
set @className = N'UserExecutor.HelloWorld'
set @methodName = N'printConsole'
EXEC sp_execute_external_script
@language = N'Dotnet'
, @script = N'UserExecutor.CallSubModule'
, @params = N'@dllLocation varchar(200),@className varchar(30),@methodName varchar(30), @Status varchar(20) OUTPUT, @rowsCount int OUTPUT'
, @Status = @Staus OUTPUT
, @rowsCount = @rowsCount OUTPUT
with result sets (( text varchar(100)));
select @rowsCount as rowsCount, @Status as message
```
6) Once you run it you should be able to see the console output in SSMS.

Просмотреть файл

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UserExecutor
{
// Lets assume that a DLL is created using this kind of code in another project.
// The DLL is moved into the folder where access is granted.
// You need to grant access to the DLL's parent folder before you execute these commands.
// To grant access to the folder, run the following commands in Windows command prompt:
// Note: The folder name may change in your environment. So please specify the right folder name.
// Command1:
// icacls "C:\Program Files\Microsoft SQL Server\MSSQL16.SQLSERVER2022\MSSQL\ExternalLibraries\6\65537\1" /grant "SQLRUsergroupSQLSERVER2022":(OI)(CI)RX /T
// Change the server name according to your naming convension .
// Command2:
// icacls "C:\Program Files\Microsoft SQL Server\MSSQL16.SQLSERVER2022\MSSQL\ExternalLibraries\6\65537\1" /grant *S-1-15-2-1:(OI)(CI)RX /T
// After executing above commands, SQL server will have access to local folder which has been specified in the commands.
// All these commands are available in OneTimeSetupCommands.cmd file in this solution as well
//
public class SubModule
{
public void printConsole()
{
Console.WriteLine("This is hello world from adhoc DLL file");
}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

Просмотреть файл

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35004.147
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubModule", "SubModule.csproj", "{9E0F73BB-A48E-4A0E-9FA6-50B58FE55ED4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9E0F73BB-A48E-4A0E-9FA6-50B58FE55ED4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E0F73BB-A48E-4A0E-9FA6-50B58FE55ED4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E0F73BB-A48E-4A0E-9FA6-50B58FE55ED4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E0F73BB-A48E-4A0E-9FA6-50B58FE55ED4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {24595C51-A808-458F-A53E-EC3E8B966D1F}
EndGlobalSection
EndGlobal