Repository navigation
Version 2: a portable HTML part, a safer sender, current SQL Server - #7
Merged
Merged
Conversation
- QueryToHtml and ConCatHtml move to a SAFE assembly of their own, which runs on SQL Server 2017+ on Windows or Linux and pairs with Database Mail - CLRSendMail stays an UNSAFE assembly for SQL Server on Windows - The setup scripts trust each assembly by its hash instead of the published key, and remove version 1's key and login once no database uses them - Profile passwords are encrypted with a certificate in the database instead of a key derived from a word in the source; EMAIL.SaveProfile manages profiles, and version 1's credentials are re-encrypted on upgrade - The server certificate is checked, mail is sent while the caller waits, and failures are errors logged in EMAIL.MonitorLog - Attachments come only from a configuration's AttachmentFolders and are saved with their content; @replyAddress works - QueryToHtml takes @params as a DECLARE list, escapes every value, keeps semicolons in literals and writes the built-in styles inline - SDK-style .NET Framework 4.8 projects; tools/SetupScript generates the setup scripts; xUnit tests on SQL Server 2022 and 2025 for Linux in CI, and on SQL Server on Windows when SQLCLR_SENDMAIL_SERVER is set
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical issues risk deleting unmigrated credentials, removing trust needed by offline databases, and allowing attachment reads outside the configured allowlist.
11 open issues
Retain trust when offline databases cannot be inspected · New Defer stale hash removal when offline databases exist · New Preserve permissions when replacing CLRSendMail functions · New Protect credential migration and column removal transactionally · New Prevent attachment reads through path reparse points · New Handle CRLF when matching source stamp lines · New Preserve permissions when recreating email functions · New Widen HostName during version 1 schema upgrades · New Preserve raw CSS text during HTML serialization · New Validate body whitespace without trimming message content · New Make post-send success logging best-effort · New
What changed in this PR
Modernizes the SQLCLR utilities for SQL Server 2017+, separating portable SAFE HTML generation from Windows-only UNSAFE SMTP sending and replacing the legacy SSDT deployment.
Changes:
- Introduces separate HTML and sender assemblies, synchronous delivery, certificate-protected passwords, and attachment allowlists.
- Adds generated, hash-trusted installation scripts with upgrade and cleanup handling.
- Adds SDK-style builds, integration tests, CI, and updated documentation and examples.
This pull request requires changes. Unresolved findings affect attachment restrictions, trust cleanup, credential migration, deployment permissions, sender behavior, HTML styling, Windows tests, and recovery documentation.
| File | Description |
|---|---|
tools/SetupScript/SourceHash.cs |
Computes setup-source freshness hashes. |
tools/SetupScript/SetupScript.csproj |
Defines the setup generator project. |
tools/SetupScript/Program.cs |
Embeds assemblies and hashes into setup scripts. |
tests/SqlClr.Tests/xunit.runner.json |
Disables parallel test collections. |
tests/SqlClr.Tests/SqlClr.Tests.csproj |
Configures integration-test dependencies. |
tests/SqlClr.Tests/Sql.cs |
Provides SQL execution and container helpers. |
tests/SqlClr.Tests/SmtpSink.cs |
Implements a local SMTP capture server. |
tests/SqlClr.Tests/SetupTests.cs |
Tests installation, reinstallation, and cleanup. |
tests/SqlClr.Tests/HtmlTests.cs |
Tests HTML behavior and setup-script freshness. |
SqlClrSendMail/SqlClrSendMail.csproj |
Defines the Windows SMTP assembly. |
SqlClrSendMail/Settings/MailProfile.cs |
Loads credentials and configures SMTP clients. |
SqlClrSendMail/Settings/MailConfiguration.cs |
Loads logging, storage, and attachment policies. |
SqlClrSendMail/Procedures/UpgradeVersion1Profiles.cs |
Migrates legacy encrypted credentials. |
SqlClrSendMail/Procedures/CLRSendMail.cs |
Implements synchronous sending and error reporting. |
SqlClrSendMail/Mail/MessageBuilder.cs |
Validates attachments and constructs messages. |
SqlClrSendMail/Mail/MailRequest.cs |
Defines message and attachment models. |
SqlClrSendMail/Logging/MailLog.cs |
Stores logs, messages, and attachment contents. |
SqlClrSendMail/CLR/PipeUtilities.cs |
Emits SQL messages and errors. |
SqlClrHtml/SqlClrHtml.csproj |
Defines the portable SAFE HTML assembly. |
SqlClrHtml/Html/TableStyle.cs |
Handles built-in and custom styles. |
SqlClrHtml/Html/Rotation.cs |
Transposes result tables. |
SqlClrHtml/Html/HtmlDocument.cs |
Constructs and serializes HTML documents. |
SqlClrHtml/Functions/QueryToHtml.cs |
Converts query results into styled HTML tables. |
SqlClrHtml/Functions/CustomSendMailHelp.cs |
Provides routine parameter documentation. |
SqlClrHtml/Functions/ConCatHtml.cs |
Combines HTML documents and stylesheets. |
SqlClrHtml/Data/ValueFormatter.cs |
Formats SQL values consistently. |
SqlClrHtml/Data/ResultTable.cs |
Represents result columns and textual rows. |
SqlClrHtml/Data/DataAccess.cs |
Reads results through the caller’s SQL context. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Test/Test2.sql |
Removes the legacy repeated-send script. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Test/Test.sql |
Removes legacy manual examples. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Test/HelpTest.sql |
Removes legacy help queries. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Test/CleanUp.sql |
Removes the legacy cleanup script. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/SqlClrCustomSendMail.sqlproj |
Removes the SSDT project. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Security/EncryptSupport.cs |
Removes source-key credential encryption. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Properties/AssemblyInfo.cs |
Removes legacy assembly metadata. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Procedures/CustomSendMail.cs |
Removes the original sender implementation. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Logging/SaveEmail.cs |
Removes obsolete persistence code. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Logging/LogEntry.cs |
Removes the legacy log writer. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Logging/EMailTracker.cs |
Removes legacy message and attachment tracking. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/HtmlSupport/RenderArray.cs |
Removes the original HTML renderer. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/HtmlSupport/DetermineStyle.cs |
Removes legacy stylesheet definitions and parsing. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Functions/QueryToHtml.cs |
Removes the superseded query renderer. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Functions/CustomSendMailHelp.cs |
Removes the original parameter-help function. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Functions/ConCatHtml.cs |
Removes the original HTML merger. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Functions/CleanMemory.cs |
Removes forced garbage collection. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Extensions/MailMessageExtensions.cs |
Removes superseded message helpers. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Database/SchemaDefinition.sql |
Removes legacy schema and security deployment. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Database/PreDeploy.sql |
Removes machine-specific key provisioning. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Database/MakeSomeNullValues.sql |
Removes legacy post-deployment configuration. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/DataAccess/DataAccess.cs |
Removes legacy querying and parameter parsing. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Configuration/SysProfiles.cs |
Removes an obsolete profile model. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Configuration/SysProfile.cs |
Removes the legacy SMTP profile wrapper. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail/Configuration/SysConfig.cs |
Removes the legacy sender configuration model. |
SqlClrCustomSendMail-master/SqlClrCustomSendMail.sln |
Removes the SSDT-era solution. |
SqlClr.slnx |
Groups replacement assemblies, tooling, and tests. |
Setup/SetupSendMail.template.sql |
Defines sender installation, upgrades, and profile management. |
Setup/SetupHtml.template.sql |
Defines HTML installation and assembly trust handling. |
Setup/CleanUp.sql |
Removes installed objects and unused server trust. |
README.md |
Documents deployment, migration, usage, and limitations. |
global.json |
Selects .NET 10 and the test runner. |
Examples/Examples.sql |
Supplies updated usage examples. |
.github/workflows/build.yml |
Builds and tests against SQL Server 2022 and 2025 containers. |
.gitattributes |
Marks embedded-assembly setup scripts as generated. |
Review effort: Balanced
| + QUOTENAME(name) + N'.sys.assembly_files AS f ON f.assembly_id = a.assembly_id AND f.file_id = 1' | ||
| + N' WHERE a.name IN (N''SimpleTalk.SQLCLR.SendMail'', N''SimpleTalk.SQLCLR.Html'')' AS nvarchar(max)), N' UNION ALL ') | ||
| FROM sys.databases | ||
| WHERE state = 0 AND HAS_DBACCESS(name) = 1 |
| + QUOTENAME(name) + N'.sys.assembly_files AS f ON f.assembly_id = a.assembly_id AND f.file_id = 1' | ||
| + N' WHERE a.name = N''SimpleTalk.SQLCLR.Html''' AS nvarchar(max)), N' UNION ALL ') | ||
| FROM sys.databases | ||
| WHERE state = 0 AND HAS_DBACCESS(name) = 1 |
Comment on lines
+57
to
+58
| IF @drop IS NOT NULL | ||
| EXEC sys.sp_executesql @drop; |
Comment on lines
+325
to
+327
| EXEC sys.sp_executesql N'EXEC EMAIL.UpgradeVersion1Profiles;'; | ||
| DROP PROCEDURE EMAIL.UpgradeVersion1Profiles; | ||
| ALTER TABLE EMAIL.Profiles DROP COLUMN UserNameV1, PasswordV1; |
Comment on lines
+32
to
+34
| var full = Path.GetFullPath(path); | ||
| if (!InFolder(full, folders)) | ||
| throw new SendMailException($"The attachment '{path}' is not in the configuration's AttachmentFolders."); |
Comment on lines
+49
to
+50
| IF @drop IS NOT NULL | ||
| EXEC sys.sp_executesql @drop; |
| EXEC sp_rename N'EMAIL.Profiles.UserName', N'UserNameV1', N'COLUMN'; | ||
| EXEC sp_rename N'EMAIL.Profiles.Password', N'PasswordV1', N'COLUMN'; | ||
| ALTER TABLE EMAIL.Profiles ADD UserName nvarchar(200) NULL, Password varbinary(512) NULL; | ||
| END; |
| : element); | ||
| } | ||
|
|
||
| public static string Write(XElement html) => html.ToString(SaveOptions.DisableFormatting); |
| { | ||
| To = Required(mailTo, "@mailTo"), | ||
| Subject = Value(mailSubject) ?? "SQLCLR Server Message", | ||
| Body = Required(mailBody, "@mailBody"), |
| PipeUtilities.Print("Sent successfully!"); | ||
|
|
||
| if (config.LoggingLevel == LoggingLevel.Maximum) | ||
| MailLog.Write(cnn, "Information", "E-mail successfully sent!", header); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Brings the repository to SQL Server 2017 and later, including 2022 and 2025. The functions and the procedure keep their names and parameters. The article's code stays available as the
v1-articletag.Two parts
QueryToHtml,ConCatHtmlandCustomSendMailHelpmove to an assembly of their own (Setup/SetupHtml.sql). It runs on Windows and on Linux, and its HTML can go out through Database Mail.CLRSendMailand the newSaveProfileare inSetup/SetupSendMail.sql. SQL Server on Linux refuses anything but SAFE assemblies, and .NET Framework'sSmtpClientneeds full trust for any port but 25. On Linux the script stops with a message saying so.Security
sp_add_trusted_assembly). There is no key file, SQLCMD mode or variables to edit.CLRSendMailruns as the owner, so callers need EXECUTE only.AttachmentFolders. Before, any file the service account could read could be mailed out.Fixes
EMAIL.MonitorLog. Version 1 sent in the background and reported nothing.@replyAddressworks.@Paramsis aDECLARElist. The old parser's bugs withbigint,varchar,decimal(p,s)and afternoon times are gone.ConCatHtmlkeeps tables of custom styles.QueryToHtmland ended by enabling a DDL trigger from the author's own database. Both setup scripts are now generated from templates.Build and tests
dotnet build.tools/SetupScript(.NET 10) generates both setup scripts. A test fails when they are older than their sources.SQLCLR_SENDMAIL_SERVERis set. They passed locally on a SQL Server 2025 LocalDB instance and are skipped in CI.The README is rewritten for version 2. It covers what changed, installing and upgrading, the parameters, and where SMTP sending from SQL Server stands today: password sign-in only, Microsoft 365's change, Database Mail, and SQL Server 2025's REST endpoint procedure.