Skip to content
Open
Show file tree
Hide file tree
Changes from 10 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
19 changes: 14 additions & 5 deletions language-extensions/dotnet-core-CSharp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ Language Extensions is a feature of SQL Server used for executing external code.

For more information about SQL Server Language Extensions, refer to this [documentation](https://docs.microsoft.com/en-us/sql/language-extensions/language-extensions-overview?view=sql-server-ver15).

The dotnet-core-CSharp-extension version in this repository is compatible with SQL Server 2019 CU3 onwards. It integrates .NET core in SQL Server and works with .NET 6.0 in **Windows only**.
The dotnet-core-CSharp-extension version in this repository is compatible with SQL Server 2019 CU3 onwards. It integrates .NET Core in SQL Server and works with .NET 8.0 and up on Windows and Linux.

Currently, the extension supports the following data types: SQL_C_SLONG, SQL_C_ULONG, SQL_C_SSHORT, SQL_C_USHORT, SQL_C_SBIGINT, SQL_C_UBIGINT, SQL_C_STINYINT, SQL_C_UTINYINT, SQL_C_BIT, SQL_C_FLOAT, SQL_C_DOUBLE, SQL_C_CHAR. It supports the following SQL data types: int, bigint, smallint, tinyint, real, float, bit, varchar(n).

To use this dotnet-core-CSharp-lang-extension.zip package, follow [this tutorial](./sample/regex/README.md). For any fixes or enhancements, you are welcome to modify, rebuild and use the binaries using the following instructions.
To use this `dotnet-core-CSharp-lang-extension.zip` (Windows) or `dotnet-core-CSharp-lang-extension.tar.gz` (Linux) package, follow [this tutorial](./sample/regex/README.md). For any fixes or enhancements, you are welcome to modify, rebuild and use the binaries using the following instructions.

## Building

Expand All @@ -24,15 +24,24 @@ To use this dotnet-core-CSharp-lang-extension.zip package, follow [this tutorial
- PATH\TO\ENLISTMENT\build-output\dotnet-core-CSharp-extension\windows\release\nativecsharpextension.dll \
- PATH\TO\ENLISTMENT\build-output\dotnet-core-CSharp-extension\windows\release\hostfxr.dll \
- PATH\TO\ENLISTMENT\build-output\dotnet-core-CSharp-extension\windows\release\Microsoft.SqlServer.CSharpExtension.dll \
- PATH\TO\ENLISTMENT\build-output\dotnet-core-CSharp-extension\windows\release\Microsoft.SqlServer.CSharpExtension.runtimeconfig.json\
- PATH\TO\ENLISTMENT\build-output\dotnet-core-CSharp-extension\windows\release\Microsoft.SqlServer.CSharpExtension.runtimeconfig.json \
- PATH\TO\ENLISTMENT\build-output\dotnet-core-CSharp-extension\windows\release\Microsoft.SqlServer.CSharpExtension.deps.json

5. Run [create-dotnet-core-CSharp-extension-zip.cmd](./build/windows/create-dotnet-core-CSharp-extension-zip.cmd) which will generate: \
- PATH\TO\ENLISTMENT\build-output\dotnet-core-CSharp-extension\target\debug\dotnet-core-CSharp-lang-extension.zip
- PATH\TO\ENLISTMENT\build-output\dotnet-core-CSharp-extension\windows\release\packages\dotnet-core-CSharp-lang-extension.zip
This zip can be used in CREATE EXTERNAL LANGUAGE, as detailed in the tutorial in the Usage section below.

### Linux
Not Supported.

1. Run [build-dotnet-core-CSharp-extension.ps1](./build/windows/build-dotnet-core-CSharp-extension.cmd) which will generate: \
- PATH/TO/ENLISTMENT/build-output/dotnet-core-CSharp-extension/linux/release/libnativecsharpextension.so \
- PATH/TO/ENLISTMENT/build-output/dotnet-core-CSharp-extension/linux/release/Microsoft.SqlServer.CSharpExtension.dll \
- PATH/TO/ENLISTMENT/build-output/dotnet-core-CSharp-extension/linux/release/Microsoft.SqlServer.CSharpExtension.runtimeconfig.json \
- PATH/TO/ENLISTMENT/build-output/dotnet-core-CSharp-extension/linux/release/Microsoft.SqlServer.CSharpExtension.deps.json

2. Run [create-dotnet-core-CSharp-extension-zip.ps1](./build/windows/create-dotnet-core-CSharp-extension-zip.cmd) which will generate: \
- PATH/TO/ENLISTMENT/build-output/dotnet-core-CSharp-extension/linux/release/packages/dotnet-core-CSharp-lang-extension.tar.gz
This tarball can be used in CREATE EXTERNAL LANGUAGE, as detailed in the tutorial in the Usage section below.

## Testing (Optional)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env pwsh
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we using powershell script for linux instead of bash script?

Copy link
Author

Choose a reason for hiding this comment

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

As written in the description, PowerShell was chosen as a scripting language to serve as a potential base for a cross-platform script. If this is not desired, I can rewrite it in bash.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do you mean this script can be used for both windows/linux now or we should have a follow-up work item for this?


# Set root and working directories
$ENL_ROOT = Join-Path $PSScriptRoot "../../../.."
$DOTNET_EXTENSION_HOME = Join-Path $ENL_ROOT "language-extensions/dotnet-core-CSharp"
$DOTNET_EXTENSION_WORKING_DIR = Join-Path $ENL_ROOT "build-output/dotnet-core-CSharp-extension/linux"

# Clean and create working directory
if (Test-Path $DOTNET_EXTENSION_WORKING_DIR) {
Remove-Item $DOTNET_EXTENSION_WORKING_DIR -Recurse -Force
}
New-Item -ItemType Directory -Path $DOTNET_EXTENSION_WORKING_DIR | Out-Null

$i = 0

# Process each build configuration
while ($true) {
$BUILD_CONFIGURATION = ""
if ($i -lt $args.Count) {
$BUILD_CONFIGURATION = $args[$i]
}
$BUILD_CONFIGURATION = $BUILD_CONFIGURATION.ToLower()
# Default to release if not debug
if ($BUILD_CONFIGURATION -ne "debug") {
$BUILD_CONFIGURATION = "release"
}

# Set target directory
$TARGET = Join-Path $ENL_ROOT "build-output/dotnet-core-CSharp-extension/target/$BUILD_CONFIGURATION"

# Clean and create target directory
if (Test-Path $TARGET) {
Remove-Item $TARGET -Recurse -Force
}
New-Item -ItemType Directory -Path $TARGET | Out-Null

Write-Host "[Info] Building dotnet-core-CSharp-extension nativecsharpextension dll..."

# Set build output directory
$BUILD_OUTPUT = Join-Path $DOTNET_EXTENSION_WORKING_DIR $BUILD_CONFIGURATION
if (Test-Path $BUILD_OUTPUT) {
Remove-Item $BUILD_OUTPUT -Recurse -Force
}
New-Item -ItemType Directory -Path $BUILD_OUTPUT | Out-Null
Push-Location $BUILD_OUTPUT

# Set source and include paths
$DOTNET_NATIVE_SRC = Join-Path $DOTNET_EXTENSION_HOME "src/native"
$DOTNET_NATIVE_INCLUDE = Join-Path $DOTNET_EXTENSION_HOME "include"
$EXTENSION_HOST_INCLUDE = Join-Path $ENL_ROOT "extension-host/include"
$DOTNET_NATIVE_LIB = Join-Path $DOTNET_EXTENSION_HOME "lib"

# Build native code
$ccArgs = @(
"-shared",
"-fPIC",
"-o", "libnativecsharpextension.so",
"-I$DOTNET_NATIVE_INCLUDE",
"-I$EXTENSION_HOST_INCLUDE",
"-DLINUX"
)
$ccArgs += Get-ChildItem -Path $DOTNET_NATIVE_SRC -Filter "*.cpp" | ForEach-Object { $_.FullName }
$ccArgs += Join-Path -Path $DOTNET_NATIVE_LIB -ChildPath "libnethost.a"
if ($BUILD_CONFIGURATION -eq "debug") {
$ccArgs += "-D", "DEBUG", "-g"
}

if ($null -ne $env:CXX) {
$cxx = $env:CXX
}
else {
$cxx = "c++"
}

$proc = Start-Process -FilePath $cxx -ArgumentList $ccArgs -NoNewWindow -Wait -PassThru
if ($proc.ExitCode -ne 0) {
Write-Host "Error: Failed to build nativecsharpextension for configuration=$BUILD_CONFIGURATION"
exit $proc.ExitCode
}

# Build managed code
Write-Host "[Info] Building Microsoft.SqlServer.CSharpExtension dll..."
$DOTNET_MANAGED_SRC = Join-Path $DOTNET_EXTENSION_HOME "src/managed"
$dotnetProc = Start-Process -FilePath "dotnet" -ArgumentList @(
"build",
(Join-Path $DOTNET_MANAGED_SRC "Microsoft.SqlServer.CSharpExtension.csproj"),
"-m",
"-c", $BUILD_CONFIGURATION,
"-o", $BUILD_OUTPUT,
"--no-dependencies"
) -NoNewWindow -Wait -PassThru

if ($dotnetProc.ExitCode -ne 0) {
Write-Host "Error: Failed to build for Microsoft.SqlServer.CSharpExtension.dll for configuration=$BUILD_CONFIGURATION"
exit $dotnetProc.ExitCode
}

Write-Host "Success: Built dotnet-core-CSharp-extension for $BUILD_CONFIGURATION configuration."

$i++
if ($i -ge $args.Count) {
break
}
}

exit 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env pwsh

$ENL_ROOT = Join-Path $PSScriptRoot "../../../.."
$DOTNET_EXTENSION_WORKING_DIR = Join-Path -Path $ENL_ROOT -ChildPath "build-output/dotnet-core-CSharp-extension/linux"

function CheckError {
param(
[int]$errorLevel,
[string]$errorMessage
)
if ($errorLevel -ne 0) {
Write-Error $errorMessage
exit $errorLevel
}
}


$i = 0

# Process each build configuration
while ($true) {
$BUILD_CONFIGURATION = ""
if ($i -lt $args.Count) {
$BUILD_CONFIGURATION = $args[$i]
}
$BUILD_CONFIGURATION = $BUILD_CONFIGURATION.ToLower()
# Default to release if not debug
if ($BUILD_CONFIGURATION -ne "debug") {
$BUILD_CONFIGURATION = "release"
}

$BUILD_OUTPUT = Join-Path -Path $DOTNET_EXTENSION_WORKING_DIR -ChildPath $BUILD_CONFIGURATION
New-Item -ItemType Directory -Force -Path "$BUILD_OUTPUT/packages" | Out-Null

# Delete the ref folder so that the zip can be loaded by the SPEES
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue "$BUILD_OUTPUT/ref"

# Define files to compress, conditionally including .pdb files if BUILD_CONFIGURATION is "debug"
$FILES_TO_COMPRESS = @(
"Microsoft.SqlServer.CSharpExtension.runtimeconfig.json",
"Microsoft.SqlServer.CSharpExtension.deps.json"
)
$FILES_TO_COMPRESS += Get-ChildItem -Path $BUILD_OUTPUT -Filter "*.dll" | ForEach-Object { $_.Name }
$FILES_TO_COMPRESS += Get-ChildItem -Path $BUILD_OUTPUT -Filter "*.so" | ForEach-Object { $_.Name }
if ($BUILD_CONFIGURATION -ieq "debug") {
$FILES_TO_COMPRESS += Get-ChildItem -Path $BUILD_OUTPUT -Filter "*.pdb" | ForEach-Object { $_.Name }
}

# Package the signed binaries.
try {
Push-Location $BUILD_OUTPUT
& tar -czvf "$BUILD_OUTPUT/packages/dotnet-core-CSharp-lang-extension.tar.gz" $FILES_TO_COMPRESS
CheckError $LASTEXITCODE "Error: Failed to create zip for dotnet-core-CSharp-extension for configuration=$BUILD_CONFIGURATION"
Pop-Location
Write-Host "Success: Compressed dotnet-core-CSharp-extension for $BUILD_CONFIGURATION configuration."
}
catch {
CheckError 1 "Error: Failed to create zip for dotnet-core-CSharp-extension for configuration=$BUILD_CONFIGURATION"
}

$i++
if ($i -ge $args.Count) {
break
}
}
21 changes: 19 additions & 2 deletions language-extensions/dotnet-core-CSharp/include/DotnetEnvironment.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,26 @@
//*********************************************************************
#pragma once

#if defined(__WIN32) || defined(WINDOWS)
#include "Windows.h"
#else
#define E_FAIL -1
#define S_OK 0
#endif

#include <string>
#include <coreclr_delegates.h>
#include <hostfxr.h>

#if defined(__WIN32) || defined(WINDOWS)
#define STR(s) L ## s
#define CH(c) L ## c
#define PATH_SEPARATOR CH('\\')
#else
#define STR(s) s
#define CH(c) c
#define PATH_SEPARATOR CH('/')
#endif

using namespace std;
using string_t = std::basic_string<char_t>;
Expand Down Expand Up @@ -46,10 +59,14 @@ class DotnetEnvironment
// Load managed assembly and get function pointer to a managed method
//
const string_t ManagedExtensionName = STR("Microsoft.SqlServer.CSharpExtension");
const string_t ManagedExtensionPath = m_root_path + STR("\\") + ManagedExtensionName + STR(".dll");
const string_t ManagedExtensionPath = m_root_path + PATH_SEPARATOR + ManagedExtensionName + STR(".dll");
const string_t ManagedExtensionType = ManagedExtensionName + STR(".CSharpExtension, ") + ManagedExtensionName;
#if defined(_WIN32) || defined(WINDOWS)
const string_t ManagedExtensionMethod = to_utf16_str(method_name);
const string_t DelegateTypeName = ManagedExtensionName + STR(".CSharpExtension+") + to_utf16_str(method_name) + STR("Delegate, ") + ManagedExtensionName;
#else
const string_t ManagedExtensionMethod = method_name;
#endif
const string_t DelegateTypeName = ManagedExtensionName + STR(".CSharpExtension+") + ManagedExtensionMethod + STR("Delegate, ") + ManagedExtensionName;
int rc = m_load_assembly_and_get_function_pointer(
ManagedExtensionPath.c_str(),
ManagedExtensionType.c_str(),
Expand Down
1 change: 0 additions & 1 deletion language-extensions/dotnet-core-CSharp/include/Logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
//*********************************************************************
#include <string>
#include <iostream>
#include <stdio.h>

using namespace std;
#define LOG(msg) Logger::Log(msg)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
#include <coreclr_delegates.h>
#include <hostfxr.h>

#if defined(__WIN32) || defined(WINDOWS)
#include <windows.h>
#endif

#include <sql.h>
#include "sqlexternallanguage.h"
#include "sqlexternallibrary.h"
Expand Down
99 changes: 99 additions & 0 deletions language-extensions/dotnet-core-CSharp/include/nethost.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#ifndef __NETHOST_H__
#define __NETHOST_H__

#include <stddef.h>

#ifdef _WIN32
#ifdef NETHOST_EXPORT
#define NETHOST_API __declspec(dllexport)
#else
// Consuming the nethost as a static library
// Shouldn't export attempt to dllimport.
#ifdef NETHOST_USE_AS_STATIC
#define NETHOST_API
#else
#define NETHOST_API __declspec(dllimport)
#endif
#endif

#define NETHOST_CALLTYPE __stdcall
#ifdef _WCHAR_T_DEFINED
typedef wchar_t char_t;
#else
typedef unsigned short char_t;
#endif
#else
#ifdef NETHOST_EXPORT
#define NETHOST_API __attribute__((__visibility__("default")))
#else
#define NETHOST_API
#endif

#define NETHOST_CALLTYPE
typedef char char_t;
#endif

#ifdef __cplusplus
extern "C" {
#endif

// Parameters for get_hostfxr_path
//
// Fields:
// size
// Size of the struct. This is used for versioning.
//
// assembly_path
// Path to the component's assembly.
// If specified, hostfxr is located as if the assembly_path is the apphost
//
// dotnet_root
// Path to directory containing the dotnet executable.
// If specified, hostfxr is located as if an application is started using
// 'dotnet app.dll', which means it will be searched for under the dotnet_root
// path and the assembly_path is ignored.
//
struct get_hostfxr_parameters {
size_t size;
const char_t *assembly_path;
const char_t *dotnet_root;
};

//
// Get the path to the hostfxr library
//
// Parameters:
// buffer
// Buffer that will be populated with the hostfxr path, including a null terminator.
//
// buffer_size
// [in] Size of buffer in char_t units.
// [out] Size of buffer used in char_t units. If the input value is too small
// or buffer is nullptr, this is populated with the minimum required size
// in char_t units for a buffer to hold the hostfxr path
//
// get_hostfxr_parameters
// Optional. Parameters that modify the behaviour for locating the hostfxr library.
// If nullptr, hostfxr is located using the environment variable or global registration
//
// Return value:
// 0 on success, otherwise failure
// 0x80008098 - buffer is too small (HostApiBufferTooSmall)
//
// Remarks:
// The full search for the hostfxr library is done on every call. To minimize the need
// to call this function multiple times, pass a large buffer (e.g. PATH_MAX).
//
NETHOST_API int NETHOST_CALLTYPE get_hostfxr_path(
char_t * buffer,
size_t * buffer_size,
const struct get_hostfxr_parameters *parameters);

#ifdef __cplusplus
} // extern "C"
#endif

#endif // __NETHOST_H__
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
Expand Down
Loading