Skip to content

Commit

Permalink
Adds codegen for table-valued parameters (#454)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnstairs authored May 10, 2019
1 parent 4a5e030 commit 5a0b402
Show file tree
Hide file tree
Showing 26 changed files with 1,468 additions and 406 deletions.
7 changes: 7 additions & 0 deletions Microsoft.Health.Fhir.sln
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Fhir.SqlSe
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Extensions.Xunit", "src\Microsoft.Health.Extensions.Xunit\Microsoft.Health.Extensions.Xunit.csproj", "{4DDF9576-E22C-460A-937F-0EE0FEA6DB87}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Health.Fhir.SqlServer.UnitTests", "src\Microsoft.Health.Fhir.SqlServer.UnitTests\Microsoft.Health.Fhir.SqlServer.UnitTests.csproj", "{E02E5224-32CD-490F-B1E5-8509AD669334}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -162,6 +164,10 @@ Global
{4DDF9576-E22C-460A-937F-0EE0FEA6DB87}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DDF9576-E22C-460A-937F-0EE0FEA6DB87}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4DDF9576-E22C-460A-937F-0EE0FEA6DB87}.Release|Any CPU.Build.0 = Release|Any CPU
{E02E5224-32CD-490F-B1E5-8509AD669334}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E02E5224-32CD-490F-B1E5-8509AD669334}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E02E5224-32CD-490F-B1E5-8509AD669334}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E02E5224-32CD-490F-B1E5-8509AD669334}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -191,6 +197,7 @@ Global
{F6ED4870-7745-4A03-BD61-2850811A0DE2} = {B70945F4-01A6-4351-955B-C4A2943B5E3B}
{87849B3F-5D12-41CA-A082-FAC065EF9FD8} = {8AD2A324-DAB5-4380-94A5-31F7D817C384}
{4DDF9576-E22C-460A-937F-0EE0FEA6DB87} = {8AD2A324-DAB5-4380-94A5-31F7D817C384}
{E02E5224-32CD-490F-B1E5-8509AD669334} = {8AD2A324-DAB5-4380-94A5-31F7D817C384}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
RESX_SortFileContentOnSave = True
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// -------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// -------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using Xunit;

namespace Microsoft.Health.Fhir.SqlServer.UnitTests
{
public class EnumerableExtensionsTests
{
[InlineData(null)]
[InlineData("")]
[Theory]
public void GivenAnNullOrEmptyEnumerable_WhenCallingNullIfEmpty_ReturnsNull(string commaSeparatedInput)
{
string[] input = commaSeparatedInput?.Split(',', StringSplitOptions.RemoveEmptyEntries);
Assert.Null(input.NullIfEmpty());
}

[InlineData("1")]
[InlineData("1,2")]
[InlineData("1,2,3")]
[Theory]
public void GivenANonEmptyEnumerable_WhenCallingNullIfEmpty_ReturnsTheExpectedSequence(string commaSeparatedInput)
{
string[] inputSequence = commaSeparatedInput?.Split(',', StringSplitOptions.RemoveEmptyEntries);
IEnumerable<string> wrappedSequence = inputSequence.NullIfEmpty();
Assert.Equal(commaSeparatedInput, string.Join(",", wrappedSequence));
Assert.Equal(commaSeparatedInput, string.Join(",", wrappedSequence));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Microsoft.Health.Fhir.SqlServer\Microsoft.Health.Fhir.SqlServer.csproj" />
</ItemGroup>

</Project>
10 changes: 10 additions & 0 deletions src/Microsoft.Health.Fhir.SqlServer/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// -------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// -------------------------------------------------------------------------------------------------

using System.Resources;
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Microsoft.Health.Fhir.SqlServer.UnitTests")]
[assembly: NeutralResourcesLanguage("en-us")]
81 changes: 81 additions & 0 deletions src/Microsoft.Health.Fhir.SqlServer/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// -------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// -------------------------------------------------------------------------------------------------

using System.Collections;
using System.Collections.Generic;

namespace Microsoft.Health.Fhir.SqlServer
{
internal static class EnumerableExtensions
{
/// <summary>
/// If the given sequence is null or empty, returns null. Otherwise returns
/// an equivalent sequence.
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="enumerable">The input sequence</param>
/// <returns>An equivalent sequence, or null if given one is empty or null.</returns>
internal static IEnumerable<T> NullIfEmpty<T>(this IEnumerable<T> enumerable)
{
if (enumerable == null)
{
return null;
}

IEnumerator<T> enumerator = enumerable.GetEnumerator();
if (!enumerator.MoveNext())
{
return null;
}

return new EnumerableFromStartedEnumerator<T>(enumerator, enumerable);
}

private class EnumerableFromStartedEnumerator<T> : IEnumerable<T>
{
private readonly IEnumerable<T> _original;
private IEnumerator<T> _startedEnumerator;

public EnumerableFromStartedEnumerator(IEnumerator<T> startedEnumerator, IEnumerable<T> original)
{
_original = original;
_startedEnumerator = startedEnumerator;
}

public IEnumerator<T> GetEnumerator()
{
IEnumerable<T> Inner(IEnumerator<T> e)
{
try
{
do
{
yield return e.Current;
}
while (e.MoveNext());
}
finally
{
e.Dispose();
}
}

if (_startedEnumerator != null)
{
IEnumerator<T> e = _startedEnumerator;
_startedEnumerator = null;
return Inner(e).GetEnumerator();
}

return _original.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)this).GetEnumerator();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,47 @@ CREATE UNIQUE NONCLUSTERED INDEX IX_Resource_ResourceTypeId_ResourceId ON dbo.Re
INCLUDE (Version)
WHERE IsHistory = 0

/*************************************************************
Capture claims on write
**************************************************************/

CREATE TABLE dbo.ClaimType
(
ClaimTypeId tinyint IDENTITY(1,1) NOT NULL,
Name varchar(128) NOT NULL
)

CREATE UNIQUE CLUSTERED INDEX IXC_Claim on dbo.ClaimType
(
Name
)

CREATE TYPE dbo.ResourceWriteClaimTableType AS TABLE
(
ClaimTypeId tinyint NOT NULL,
ClaimValue nvarchar(128) NOT NULL
)

CREATE TABLE dbo.ResourceWriteClaim
(
ResourceSurrogateId bigint NOT NULL,
ClaimTypeId tinyint NOT NULL,
ClaimValue nvarchar(128) NOT NULL,
) WITH (DATA_COMPRESSION = PAGE)

CREATE CLUSTERED INDEX IXC_LastModifiedClaim on dbo.ResourceWriteClaim
(
ResourceSurrogateId,
ClaimTypeId
)


GO

/*************************************************************
Sequence for generating surrogate IDs for resources
**************************************************************/

CREATE SEQUENCE dbo.ResourceSurrogateIdSequence
AS BIGINT
START WITH 0
Expand Down Expand Up @@ -231,6 +270,8 @@ GO
-- * The HTTP method/verb used for the request
-- @rawResource
-- * A compressed UTF16-encoded JSON document
-- @resourceWriteClaims
-- * claims on the principal that performed the write
--
-- RETURN VALUE
-- The version of the resource as a result set. Will be empty if no insertion was done.
Expand All @@ -244,7 +285,8 @@ CREATE PROCEDURE dbo.UpsertResource
@updatedDateTime datetimeoffset(7),
@keepHistory bit,
@requestMethod varchar(10),
@rawResource varbinary(max)
@rawResource varbinary(max),
@resourceWriteClaims dbo.ResourceWriteClaimTableType READONLY
AS
SET NOCOUNT ON

Expand Down Expand Up @@ -305,6 +347,11 @@ AS
VALUES
(@resourceTypeId, @resourceId, @version, 0, @resourceSurrogateId, CONVERT(datetime2(7), @updatedDateTime), @isDeleted, @requestMethod, @rawResource)

INSERT INTO dbo.ResourceWriteClaim
(ResourceSurrogateId, ClaimTypeId, ClaimValue)
SELECT @resourceSurrogateId, ClaimTypeId, ClaimValue from @resourceWriteClaims


select @version

COMMIT TRANSACTION
Expand Down Expand Up @@ -369,8 +416,15 @@ AS
SET XACT_ABORT ON
BEGIN TRANSACTION

DECLARE @resourceSurrogateIds TABLE(ResourceSurrogateId bigint NOT NULL)

DELETE FROM dbo.Resource
OUTPUT deleted.ResourceSurrogateId
INTO @resourceSurrogateIds
WHERE ResourceTypeId = @resourceTypeId AND ResourceId = @resourceId

DELETE FROM dbo.ResourceWriteClaim
WHERE ResourceSurrogateId IN (SELECT ResourceSurrogateId FROM @resourceSurrogateIds)

COMMIT TRANSACTION
GO
Loading

0 comments on commit 5a0b402

Please sign in to comment.