Deployment
A SQLCLR deployment you cannot rerun from a script is a liability with a version number. Everything on this page serves one goal: any engineer, any environment, same result.
The deploy script shape
Order matters twice. Building up: assembly first, then the T-SQL modules that bind to it (and referenced assemblies before the assemblies that reference them). Tearing down is the exact reverse; the engine refuses to drop an assembly while modules still bind to it.
-- Teardown, idempotent ('FS' = CLR scalar function)
IF OBJECT_ID(N'dbo.RegexIsMatch', N'FS') IS NOT NULL
DROP FUNCTION dbo.RegexIsMatch;
IF EXISTS (SELECT 1 FROM sys.assemblies WHERE name = N'SqlClrExamples')
DROP ASSEMBLY SqlClrExamples;
-- Build up
CREATE ASSEMBLY SqlClrExamples
FROM 0x4D5A90000300000004000000FFFF0000... -- full DLL as a binary literal
WITH PERMISSION_SET = SAFE;
CREATE FUNCTION dbo.RegexIsMatch (@input NVARCHAR(MAX), @pattern NVARCHAR(4000))
RETURNS BIT
AS EXTERNAL NAME SqlClrExamples.RegexScalar.IsMatch;
ALTER ASSEMBLY for in-place upgrades
Dropping an assembly means dropping every bound object, and its permissions, and anything scripted against it. When the new build keeps the same public method signatures, swap the bits in place instead:
ALTER ASSEMBLY SqlClrExamples
FROM 0x4D5A90000300000004000000FFFF0000...; -- the new build
Rules of the road:
- Methods referenced by cataloged modules must still exist with matching signatures; otherwise the statement fails and names the binding it broke.
- If persisted computed columns or indexes depend on the assembly's
functions, add
WITH UNCHECKED DATAand schedule a verification pass; you are asserting the new code computes the same values as the old. - Under strict security the new bits need trust too. Register the new
hash before the
ALTER, or sign; signatures survive rebuilds. - The app domain recycles on next use. Expect one cold call.
Binary literals beat file paths
FROM N'C:\clr\...' requires the engine's service account to read that
path at deploy time. That is fine on a workstation, impossible on Managed
Instance, awkward in containers, and a silent dependency on a filesystem
being in the right state. Embedding the assembly as hex makes the script
self-contained and source-controllable:
$bytes = [System.IO.File]::ReadAllBytes("bin\Release\SqlClrExamples.dll")
$hex = '0x' + [System.BitConverter]::ToString($bytes).Replace('-', '')
$hash = (Get-FileHash "bin\Release\SqlClrExamples.dll" -Algorithm SHA512).Hash
# splice $hex and "0x$hash" into the migration script template
A 100 KB assembly becomes a 200 KB script. SSMS grumbles; sqlcmd does not
care.
CI/CD
The pipeline shape I deploy everywhere:
- Build deterministically. Pin the compiler and target framework, so the hash is reproducible from source.
- Generate the migration script:
sp_add_trusted_assemblywith the new hash (skip if signing),CREATEorALTER ASSEMBLYwith embedded bits, then module DDL. - Apply with
sqlcmdor your migration runner. The script is plain T-SQL, so Flyway and DbUp are perfectly happy. - Verify that deployed bits match built bits:
SELECT a.name, HASHBYTES('SHA2_512', af.content) AS deployed_hash
FROM sys.assemblies AS a
JOIN sys.assembly_files AS af ON af.assembly_id = a.assembly_id
WHERE a.is_user_defined = 1;
- Smoke-test one call per module. A single
SELECT dbo.RegexIsMatch(...)in the pipeline has caught more bad deploys than any amount of review.
Upgrading SQL Server with assemblies in place
Assemblies are database content, so engine upgrades carry them forward; the IL does not need recompiling. What does need attention:
- Crossing into 2017 or later:
clr strict securityturns on, and unsigned, unlisted assemblies stop loading. Sign or allowlist before the upgrade, not during the incident afterward. - Ancient estates (2005 through 2008 R2) hosted CLR 2.0; SQL Server 2012 onward hosts CLR 4. Compatibility is good, but retest anything that touches serialization or culture-sensitive string handling.
- Run the pipeline's smoke tests against the new version before cutover. Same tests, new engine.
Linux, containers, Kubernetes
SAFEonly. Audit before replatforming:SELECT name, permission_set_desc FROM sys.assemblies WHERE is_user_defined = 1;AnythingEXTERNAL_ACCESSorUNSAFEneeds a redesign, not a lift.- Deploy from binary literals. A container filesystem is not a deploy target.
- The assembly travels inside the database, so pod restarts, failovers, and replica seeding need nothing extra. The server-scoped trust registrations do: apply trusted-assembly hashes and signing logins to every replica and node image, or the first failover becomes a CLR outage (Security).
Signed, audited, Windows plus Linux plus k8s. The same bits everywhere is the entire appeal.