diff --git a/Directory.Packages.props b/Directory.Packages.props
index f4e78d27e8..584100c9cc 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -6,6 +6,7 @@
+
@@ -125,4 +126,4 @@
-
+
\ No newline at end of file
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Migrations/85.diff.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Migrations/85.diff.sql
new file mode 100644
index 0000000000..d4cfaf9bf8
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Migrations/85.diff.sql
@@ -0,0 +1,3928 @@
+IF NOT EXISTS (SELECT * FROM sys.sequences WHERE name = 'ResourceIdIntMapSequence')
+CREATE SEQUENCE dbo.ResourceIdIntMapSequence
+ AS int
+ START WITH 0
+ INCREMENT BY 1
+ MINVALUE 0
+ MAXVALUE 79999
+ CYCLE
+ CACHE 1000000
+GO
+CREATE OR ALTER PROCEDURE dbo.AssignResourceIdInts @Count int, @FirstIdInt bigint OUT
+AS
+set nocount on
+DECLARE @SP varchar(100) = 'AssignResourceIdInts'
+ ,@Mode varchar(200) = 'Cnt='+convert(varchar,@Count)
+ ,@st datetime = getUTCdate()
+ ,@FirstValueVar sql_variant
+ ,@LastValueVar sql_variant
+ ,@SequenceRangeFirstValue int
+
+BEGIN TRY
+ SET @FirstValueVar = NULL
+ WHILE @FirstValueVar IS NULL
+ BEGIN
+ EXECUTE sys.sp_sequence_get_range @sequence_name = 'dbo.ResourceIdIntMapSequence', @range_size = @Count, @range_first_value = @FirstValueVar OUT, @range_last_value = @LastValueVar OUT
+ SET @SequenceRangeFirstValue = convert(int,@FirstValueVar)
+ IF @SequenceRangeFirstValue > convert(int,@LastValueVar)
+ SET @FirstValueVar = NULL
+ END
+
+ SET @FirstIdInt = datediff_big(millisecond,'0001-01-01',sysUTCdatetime()) * 80000 + @SequenceRangeFirstValue
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+ IF @@trancount > 0 ROLLBACK TRANSACTION
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Error';
+ THROW
+END CATCH
+GO
+IF NOT EXISTS (SELECT * FROM sys.types WHERE name = 'ResourceListLake')
+CREATE TYPE dbo.ResourceListLake AS TABLE
+(
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL
+ ,Version int NOT NULL
+ ,HasVersionToCompare bit NOT NULL -- in case of multiple versions per resource indicates that row contains (existing version + 1) value
+ ,IsDeleted bit NOT NULL
+ ,IsHistory bit NOT NULL
+ ,KeepHistory bit NOT NULL
+ ,RawResource varbinary(max) NULL
+ ,IsRawResourceMetaSet bit NOT NULL
+ ,RequestMethod varchar(10) NULL
+ ,SearchParamHash varchar(64) NULL
+ ,FileId bigint NULL
+ ,OffsetInFile int NULL
+
+ PRIMARY KEY (ResourceTypeId, ResourceSurrogateId)
+ ,UNIQUE (ResourceTypeId, ResourceId, Version)
+)
+GO
+IF object_id('ResourceIdIntMap') IS NULL
+BEGIN
+ CREATE TABLE dbo.ResourceIdIntMap
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceIdInt bigint NOT NULL
+ ,ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL
+
+ CONSTRAINT PKC_ResourceIdIntMap_ResourceIdInt_ResourceTypeId PRIMARY KEY CLUSTERED (ResourceIdInt, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+ ,CONSTRAINT U_ResourceIdIntMap_ResourceId_ResourceTypeId UNIQUE (ResourceId, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+ )
+
+ ALTER TABLE dbo.ResourceIdIntMap SET ( LOCK_ESCALATION = AUTO )
+END
+GO
+IF object_id('RawResources') IS NULL
+BEGIN
+ CREATE TABLE dbo.RawResources
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,RawResource varbinary(max) NULL
+
+ CONSTRAINT PKC_RawResources_ResourceTypeId_ResourceSurrogateId PRIMARY KEY CLUSTERED (ResourceTypeId, ResourceSurrogateId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+ )
+
+ ALTER TABLE dbo.RawResources SET ( LOCK_ESCALATION = AUTO )
+END
+GO
+IF object_id('CurrentResources') IS NULL
+BEGIN
+ CREATE TABLE dbo.CurrentResources
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,ResourceIdInt bigint NOT NULL
+ ,Version int NOT NULL
+ ,IsHistory bit NOT NULL CONSTRAINT DF_CurrentResources_IsHistory DEFAULT 0, CONSTRAINT CH_CurrentResources_IsHistory CHECK (IsHistory = 0)
+ ,IsDeleted bit NOT NULL
+ ,RequestMethod varchar(10) NULL
+ ,IsRawResourceMetaSet bit NOT NULL CONSTRAINT DF_CurrentResources_IsRawResourceMetaSet DEFAULT 0
+ ,SearchParamHash varchar(64) NULL
+ ,TransactionId bigint NULL
+ ,HistoryTransactionId bigint NULL
+ ,FileId bigint NULL
+ ,OffsetInFile int NULL
+
+ CONSTRAINT PKC_CurrentResources_ResourceTypeId_ResourceSurrogateId PRIMARY KEY CLUSTERED (ResourceTypeId, ResourceSurrogateId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+ ,CONSTRAINT U_CurrentResources_ResourceIdInt_ResourceTypeId UNIQUE (ResourceIdInt, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+ )
+
+ ALTER TABLE dbo.CurrentResources ADD CONSTRAINT FK_CurrentResources_ResourceIdInt_ResourceTypeId_ResourceIdIntMap FOREIGN KEY (ResourceIdInt, ResourceTypeId) REFERENCES dbo.ResourceIdIntMap (ResourceIdInt, ResourceTypeId)
+
+ ALTER TABLE dbo.CurrentResources SET ( LOCK_ESCALATION = AUTO )
+
+ CREATE INDEX IX_TransactionId_ResourceTypeId_WHERE_TransactionId_NOT_NULL ON dbo.CurrentResources (TransactionId, ResourceTypeId) WHERE TransactionId IS NOT NULL WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+ CREATE INDEX IX_HistoryTransactionId_ResourceTypeId_WHERE_HistoryTransactionId_NOT_NULL ON dbo.CurrentResources (HistoryTransactionId, ResourceTypeId) WHERE HistoryTransactionId IS NOT NULL WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+END
+GO
+IF object_id('HistoryResources') IS NULL
+BEGIN
+ CREATE TABLE dbo.HistoryResources
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,ResourceIdInt bigint NOT NULL
+ ,Version int NOT NULL
+ ,IsHistory bit NOT NULL CONSTRAINT DF_HistoryResources_IsHistory DEFAULT 1, CONSTRAINT CH_HistoryResources_IsHistory CHECK (IsHistory = 1)
+ ,IsDeleted bit NOT NULL
+ ,RequestMethod varchar(10) NULL
+ ,IsRawResourceMetaSet bit NOT NULL CONSTRAINT DF_HistoryResources_IsRawResourceMetaSet DEFAULT 0
+ ,SearchParamHash varchar(64) NULL
+ ,TransactionId bigint NULL
+ ,HistoryTransactionId bigint NULL
+ ,FileId bigint NULL
+ ,OffsetInFile int NULL
+
+ CONSTRAINT PKC_HistoryResources_ResourceTypeId_ResourceSurrogateId PRIMARY KEY CLUSTERED (ResourceTypeId, ResourceSurrogateId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+ ,CONSTRAINT U_HistoryResources_ResourceIdInt_Version_ResourceTypeId UNIQUE (ResourceIdInt, Version, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+ )
+
+ ALTER TABLE dbo.HistoryResources ADD CONSTRAINT FK_HistoryResources_ResourceIdInt_ResourceTypeId_ResourceIdIntMap FOREIGN KEY (ResourceIdInt, ResourceTypeId) REFERENCES dbo.ResourceIdIntMap (ResourceIdInt, ResourceTypeId)
+
+ ALTER TABLE dbo.HistoryResources SET ( LOCK_ESCALATION = AUTO )
+
+ CREATE INDEX IX_TransactionId_ResourceTypeId_WHERE_TransactionId_NOT_NULL ON dbo.HistoryResources (TransactionId, ResourceTypeId) WHERE TransactionId IS NOT NULL WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+ CREATE INDEX IX_HistoryTransactionId_ResourceTypeId_WHERE_HistoryTransactionId_NOT_NULL ON dbo.HistoryResources (HistoryTransactionId, ResourceTypeId) WHERE HistoryTransactionId IS NOT NULL WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+END
+GO
+IF object_id('ResourceReferenceSearchParams') IS NULL
+BEGIN
+ CREATE TABLE dbo.ResourceReferenceSearchParams
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,SearchParamId smallint NOT NULL
+ ,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
+ ,ReferenceResourceTypeId smallint NOT NULL
+ ,ReferenceResourceIdInt bigint NOT NULL
+ ,IsResourceRef bit NOT NULL CONSTRAINT DF_ResourceReferenceSearchParams_IsResourceRef DEFAULT 1, CONSTRAINT CH_ResourceReferenceSearchParams_IsResourceRef CHECK (IsResourceRef = 1)
+ )
+
+ ALTER TABLE dbo.ResourceReferenceSearchParams ADD CONSTRAINT FK_ResourceReferenceSearchParams_ReferenceResourceIdInt_ReferenceResourceTypeId_ResourceIdIntMap FOREIGN KEY (ReferenceResourceIdInt, ReferenceResourceTypeId) REFERENCES dbo.ResourceIdIntMap (ResourceIdInt, ResourceTypeId)
+
+ ALTER TABLE dbo.ResourceReferenceSearchParams SET ( LOCK_ESCALATION = AUTO )
+
+ CREATE CLUSTERED INDEX IXC_ResourceSurrogateId_SearchParamId_ResourceTypeId
+ ON dbo.ResourceReferenceSearchParams (ResourceSurrogateId, SearchParamId, ResourceTypeId)
+ WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+
+ CREATE UNIQUE INDEX IXU_ReferenceResourceIdInt_ReferenceResourceTypeId_SearchParamId_BaseUri_ResourceSurrogateId_ResourceTypeId
+ ON dbo.ResourceReferenceSearchParams (ReferenceResourceIdInt, ReferenceResourceTypeId, SearchParamId, BaseUri, ResourceSurrogateId, ResourceTypeId)
+ WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+END
+GO
+IF object_id('StringReferenceSearchParams') IS NULL
+BEGIN
+ CREATE TABLE dbo.StringReferenceSearchParams
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,SearchParamId smallint NOT NULL
+ ,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
+ ,ReferenceResourceId varchar(768) COLLATE Latin1_General_100_CS_AS NOT NULL
+ ,IsResourceRef bit NOT NULL CONSTRAINT DF_StringReferenceSearchParams_IsResourceRef DEFAULT 0, CONSTRAINT CH_StringReferenceSearchParams_IsResourceRef CHECK (IsResourceRef = 0)
+ )
+
+ ALTER TABLE dbo.StringReferenceSearchParams SET ( LOCK_ESCALATION = AUTO )
+
+ CREATE CLUSTERED INDEX IXC_ResourceSurrogateId_SearchParamId_ResourceTypeId
+ ON dbo.StringReferenceSearchParams (ResourceSurrogateId, SearchParamId, ResourceTypeId)
+ WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+
+ CREATE UNIQUE INDEX IXU_ReferenceResourceId_SearchParamId_BaseUri_ResourceSurrogateId_ResourceTypeId
+ ON dbo.StringReferenceSearchParams (ReferenceResourceId, SearchParamId, BaseUri, ResourceSurrogateId, ResourceTypeId)
+ WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+END
+GO
+IF EXISTS (SELECT * FROM sys.objects WHERE name = 'Resource' AND type = 'u')
+BEGIN
+ BEGIN TRY
+ BEGIN TRANSACTION
+
+ EXECUTE sp_rename 'Resource', 'ResourceTbl'
+
+ EXECUTE('-- Resource
+CREATE VIEW dbo.Resource
+AS
+SELECT A.ResourceTypeId
+ ,A.ResourceSurrogateId
+ ,ResourceId
+ ,A.ResourceIdInt
+ ,Version
+ ,IsHistory
+ ,IsDeleted
+ ,RequestMethod
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,TransactionId
+ ,HistoryTransactionId
+ ,FileId
+ ,OffsetInFile
+ FROM dbo.CurrentResources A
+ LEFT OUTER JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ LEFT OUTER JOIN dbo.ResourceIdIntMap C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = A.ResourceIdInt
+UNION ALL
+SELECT A.ResourceTypeId
+ ,A.ResourceSurrogateId
+ ,ResourceId
+ ,A.ResourceIdInt
+ ,Version
+ ,IsHistory
+ ,IsDeleted
+ ,RequestMethod
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,TransactionId
+ ,HistoryTransactionId
+ ,FileId
+ ,OffsetInFile
+ FROM dbo.HistoryResources A
+ LEFT OUTER JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ LEFT OUTER JOIN dbo.ResourceIdIntMap C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = A.ResourceIdInt
+UNION ALL
+SELECT ResourceTypeId
+ ,ResourceSurrogateId
+ ,ResourceId
+ ,NULL
+ ,Version
+ ,IsHistory
+ ,IsDeleted
+ ,RequestMethod
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,TransactionId
+ ,HistoryTransactionId
+ ,NULL
+ ,NULL
+ FROM dbo.ResourceTbl
+ ')
+
+ EXECUTE('-- CurrentResource
+ALTER VIEW dbo.CurrentResource
+AS
+SELECT A.ResourceTypeId
+ ,A.ResourceSurrogateId
+ ,ResourceId
+ ,A.ResourceIdInt
+ ,Version
+ ,IsHistory
+ ,IsDeleted
+ ,RequestMethod
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,TransactionId
+ ,HistoryTransactionId
+ ,FileId
+ ,OffsetInFile
+ FROM dbo.CurrentResources A
+ LEFT OUTER JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ LEFT OUTER JOIN dbo.ResourceIdIntMap C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = A.ResourceIdInt
+UNION ALL
+SELECT ResourceTypeId
+ ,ResourceSurrogateId
+ ,ResourceId
+ ,NULL
+ ,Version
+ ,IsHistory
+ ,IsDeleted
+ ,RequestMethod
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,TransactionId
+ ,HistoryTransactionId
+ ,NULL
+ ,NULL
+ FROM dbo.ResourceTbl
+ WHERE IsHistory = 0
+ ')
+
+ EXECUTE('-- ResourceIns
+CREATE TRIGGER dbo.ResourceIns ON dbo.Resource INSTEAD OF INSERT
+AS
+BEGIN
+ INSERT INTO dbo.RawResources
+ ( ResourceTypeId, ResourceSurrogateId, RawResource )
+ SELECT ResourceTypeId, ResourceSurrogateId, RawResource
+ FROM Inserted A
+ WHERE RawResource IS NOT NULL
+ AND NOT EXISTS (SELECT * FROM dbo.RawResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId)
+
+ INSERT INTO dbo.CurrentResources
+ ( ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile )
+ SELECT ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile
+ FROM Inserted A
+ WHERE IsHistory = 0
+ AND NOT EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId)
+
+ INSERT INTO dbo.HistoryResources
+ ( ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile )
+ SELECT ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile
+ FROM Inserted A
+ WHERE IsHistory = 1
+ AND NOT EXISTS (SELECT * FROM dbo.HistoryResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId)
+END
+ ')
+
+ EXECUTE('-- ResourceUpd
+CREATE OR ALTER TRIGGER dbo.ResourceUpd ON dbo.Resource INSTEAD OF UPDATE
+AS
+BEGIN
+ IF UPDATE(IsDeleted) AND UPDATE(RawResource) AND UPDATE(SearchParamHash) AND UPDATE(HistoryTransactionId) AND NOT UPDATE(IsHistory) -- hard delete resource
+ BEGIN
+ UPDATE B
+ SET IsDeleted = A.IsDeleted
+ ,SearchParamHash = A.SearchParamHash
+ ,HistoryTransactionId = A.HistoryTransactionId
+ ,RawResource = A.RawResource
+ FROM Inserted A
+ JOIN dbo.ResourceTbl B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ UPDATE B
+ SET RawResource = A.RawResource
+ FROM Inserted A
+ JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ IF @@rowcount = 0
+ INSERT INTO dbo.RawResources
+ ( ResourceTypeId, ResourceSurrogateId, RawResource )
+ SELECT ResourceTypeId, ResourceSurrogateId, RawResource
+ FROM Inserted
+ WHERE RawResource IS NOT NULL
+
+ UPDATE B
+ SET IsDeleted = A.IsDeleted
+ ,SearchParamHash = A.SearchParamHash
+ ,HistoryTransactionId = A.HistoryTransactionId
+ FROM Inserted A
+ JOIN dbo.CurrentResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ RETURN
+ END
+
+ IF UPDATE(SearchParamHash) AND NOT UPDATE(IsHistory) -- reindex
+ BEGIN
+ UPDATE B
+ SET SearchParamHash = A.SearchParamHash
+ FROM Inserted A
+ JOIN dbo.ResourceTbl B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ WHERE A.IsHistory = 0
+
+ UPDATE B
+ SET SearchParamHash = A.SearchParamHash
+ FROM Inserted A
+ JOIN dbo.CurrentResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ WHERE A.IsHistory = 0
+
+ RETURN
+ END
+
+ IF UPDATE(TransactionId) AND NOT UPDATE(IsHistory) -- cleanup trans
+ BEGIN
+ UPDATE B
+ SET TransactionId = A.TransactionId
+ FROM Inserted A
+ JOIN dbo.ResourceTbl B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ UPDATE B
+ SET TransactionId = A.TransactionId
+ FROM Inserted A
+ JOIN dbo.CurrentResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 0
+
+ UPDATE B
+ SET TransactionId = A.TransactionId
+ FROM Inserted A
+ JOIN dbo.HistoryResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 1
+
+ RETURN
+ END
+
+ IF UPDATE(RawResource) -- invisible records
+ BEGIN
+ UPDATE B
+ SET RawResource = A.RawResource
+ FROM Inserted A
+ JOIN dbo.ResourceTbl B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ UPDATE B
+ SET RawResource = A.RawResource
+ FROM Inserted A
+ JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ IF @@rowcount = 0
+ INSERT INTO dbo.RawResources
+ ( ResourceTypeId, ResourceSurrogateId, RawResource )
+ SELECT ResourceTypeId, ResourceSurrogateId, RawResource
+ FROM Inserted
+ WHERE RawResource IS NOT NULL
+ END
+
+ IF NOT UPDATE(IsHistory)
+ RAISERROR(''Generic updates are not supported via Resource view'',18,127)
+
+ UPDATE A
+ SET IsHistory = 1
+ FROM dbo.ResourceTbl A
+ WHERE EXISTS (SELECT * FROM Inserted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 1)
+
+ DELETE FROM A
+ FROM dbo.CurrentResources A
+ WHERE EXISTS (SELECT * FROM Inserted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 1)
+
+ INSERT INTO dbo.HistoryResources
+ ( ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile )
+ SELECT ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile
+ FROM Inserted
+ WHERE IsHistory = 1
+ AND ResourceIdInt IS NOT NULL
+END
+ ')
+
+ EXECUTE('-- ResourceDel
+CREATE TRIGGER dbo.ResourceDel ON dbo.Resource INSTEAD OF DELETE
+AS
+BEGIN
+ DELETE FROM A
+ FROM dbo.ResourceTbl A
+ WHERE EXISTS (SELECT * FROM Deleted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId)
+
+ DELETE FROM A
+ FROM dbo.CurrentResources A
+ WHERE EXISTS (SELECT * FROM Deleted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 0)
+
+ DELETE FROM A
+ FROM dbo.HistoryResources A
+ WHERE EXISTS (SELECT * FROM Deleted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 1)
+
+ DELETE FROM A
+ FROM dbo.RawResources A
+ WHERE EXISTS (SELECT * FROM Deleted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId)
+END
+ ')
+
+ EXECUTE('-- GetResources
+ALTER PROCEDURE dbo.GetResources @ResourceKeys dbo.ResourceKeyList READONLY
+AS
+set nocount on
+DECLARE @st datetime = getUTCdate()
+ ,@SP varchar(100) = ''GetResources''
+ ,@InputRows int
+ ,@DummyTop bigint = 9223372036854775807
+ ,@NotNullVersionExists bit
+ ,@NullVersionExists bit
+ ,@MinRT smallint
+ ,@MaxRT smallint
+
+SELECT @MinRT = min(ResourceTypeId), @MaxRT = max(ResourceTypeId), @InputRows = count(*), @NotNullVersionExists = max(CASE WHEN Version IS NOT NULL THEN 1 ELSE 0 END), @NullVersionExists = max(CASE WHEN Version IS NULL THEN 1 ELSE 0 END) FROM @ResourceKeys
+
+DECLARE @Mode varchar(100) = ''RT=[''+convert(varchar,@MinRT)+'',''+convert(varchar,@MaxRT)+''] Cnt=''+convert(varchar,@InputRows)+'' NNVE=''+convert(varchar,@NotNullVersionExists)+'' NVE=''+convert(varchar,@NullVersionExists)
+
+BEGIN TRY
+ IF @NotNullVersionExists = 1
+ IF @NullVersionExists = 0
+ SELECT *
+ FROM (SELECT B.ResourceTypeId
+ ,B.ResourceId
+ ,ResourceSurrogateId
+ ,C.Version
+ ,IsDeleted
+ ,IsHistory
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,FileId
+ ,OffsetInFile
+ FROM (SELECT * FROM @ResourceKeys) A
+ INNER LOOP JOIN dbo.ResourceIdIntMap B WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN dbo.Resource C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = B.ResourceIdInt AND C.Version = A.Version
+ UNION ALL
+ SELECT B.ResourceTypeId
+ ,B.ResourceId
+ ,ResourceSurrogateId
+ ,B.Version
+ ,IsDeleted
+ ,IsHistory
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,NULL
+ ,NULL
+ FROM (SELECT TOP (@DummyTop) * FROM @ResourceKeys) A
+ JOIN dbo.ResourceTbl B WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId_Version) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.Version = A.Version
+ ) A
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ ELSE
+ SELECT *
+ FROM (SELECT B.ResourceTypeId
+ ,B.ResourceId
+ ,ResourceSurrogateId
+ ,C.Version
+ ,IsDeleted
+ ,IsHistory
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,FileId
+ ,OffsetInFile
+ FROM (SELECT * FROM @ResourceKeys WHERE Version IS NOT NULL) A
+ INNER LOOP JOIN dbo.ResourceIdIntMap B WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN dbo.Resource C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = B.ResourceIdInt AND C.Version = A.Version
+ UNION ALL
+ SELECT B.ResourceTypeId
+ ,B.ResourceId
+ ,C.ResourceSurrogateId
+ ,C.Version
+ ,IsDeleted
+ ,IsHistory
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,FileId
+ ,OffsetInFile
+ FROM (SELECT * FROM @ResourceKeys WHERE Version IS NULL) A
+ INNER LOOP JOIN dbo.ResourceIdIntMap B WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN dbo.CurrentResources C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = B.ResourceIdInt AND C.IsHistory = 0
+ LEFT OUTER JOIN dbo.RawResources D ON D.ResourceTypeId = A.ResourceTypeId AND D.ResourceSurrogateId = C.ResourceSurrogateId
+ UNION ALL
+ SELECT B.ResourceTypeId
+ ,B.ResourceId
+ ,ResourceSurrogateId
+ ,B.Version
+ ,IsDeleted
+ ,IsHistory
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,NULL
+ ,NULL
+ FROM (SELECT TOP (@DummyTop) * FROM @ResourceKeys WHERE Version IS NOT NULL) A
+ JOIN dbo.ResourceTbl B WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId_Version) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.Version = A.Version
+ UNION ALL
+ SELECT B.ResourceTypeId
+ ,B.ResourceId
+ ,ResourceSurrogateId
+ ,B.Version
+ ,IsDeleted
+ ,IsHistory
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,NULL
+ ,NULL
+ FROM (SELECT TOP (@DummyTop) * FROM @ResourceKeys WHERE Version IS NULL) A
+ JOIN dbo.ResourceTbl B WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ WHERE IsHistory = 0
+ ) A
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ ELSE
+ SELECT *
+ FROM (SELECT B.ResourceTypeId
+ ,B.ResourceId
+ ,C.ResourceSurrogateId
+ ,C.Version
+ ,IsDeleted
+ ,IsHistory
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,FileId
+ ,OffsetInFile
+ FROM (SELECT * FROM @ResourceKeys) A
+ INNER LOOP JOIN dbo.ResourceIdIntMap B WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN dbo.CurrentResources C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = B.ResourceIdInt
+ LEFT OUTER JOIN dbo.RawResources D ON D.ResourceTypeId = A.ResourceTypeId AND D.ResourceSurrogateId = C.ResourceSurrogateId
+ UNION ALL
+ SELECT B.ResourceTypeId
+ ,B.ResourceId
+ ,ResourceSurrogateId
+ ,B.Version
+ ,IsDeleted
+ ,IsHistory
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,NULL
+ ,NULL
+ FROM (SELECT TOP (@DummyTop) * FROM @ResourceKeys) A
+ JOIN dbo.ResourceTbl B WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ WHERE IsHistory = 0
+ ) A
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Start=@st,@Rows=@@rowcount
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error'',@Start=@st;
+ THROW
+END CATCH
+ ')
+
+ EXECUTE sp_rename 'ReferenceSearchParam', 'ReferenceSearchParamTbl'
+
+ EXECUTE('-- ReferenceSearchParam
+CREATE VIEW dbo.ReferenceSearchParam
+AS
+SELECT A.ResourceTypeId
+ ,ResourceSurrogateId
+ ,SearchParamId
+ ,BaseUri
+ ,ReferenceResourceTypeId
+ ,ReferenceResourceId = B.ResourceId
+ ,ReferenceResourceIdInt
+ ,IsResourceRef
+ FROM dbo.ResourceReferenceSearchParams A
+ LEFT OUTER JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ReferenceResourceTypeId AND B.ResourceIdInt = A.ReferenceResourceIdInt
+UNION ALL
+SELECT ResourceTypeId
+ ,ResourceSurrogateId
+ ,SearchParamId
+ ,BaseUri
+ ,NULL
+ ,ReferenceResourceId
+ ,NULL
+ ,IsResourceRef
+ FROM dbo.StringReferenceSearchParams
+UNION ALL
+SELECT ResourceTypeId
+ ,ResourceSurrogateId
+ ,SearchParamId
+ ,BaseUri
+ ,ReferenceResourceTypeId
+ ,ReferenceResourceId
+ ,NULL
+ ,NULL
+ FROM dbo.ReferenceSearchParamTbl
+ ')
+
+ EXECUTE('-- ReferenceSearchParamDel
+CREATE TRIGGER dbo.ReferenceSearchParamDel ON dbo.ReferenceSearchParam INSTEAD OF DELETE
+AS
+BEGIN
+ DELETE FROM A
+ FROM dbo.ResourceReferenceSearchParams A
+ WHERE EXISTS (SELECT * FROM Deleted B WHERE B.IsResourceRef = 1 AND B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId)
+
+ DELETE FROM A
+ FROM dbo.StringReferenceSearchParams A
+ WHERE EXISTS (SELECT * FROM Deleted B WHERE B.IsResourceRef = 0 AND B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId)
+END
+ ')
+
+ DROP PROCEDURE CaptureResourceIdsForChanges
+ DROP PROCEDURE MergeResources
+ DROP PROCEDURE UpdateResourceSearchParams
+ DROP TYPE ReferenceSearchParamList
+
+ EXECUTE('-- ReferenceSearchParamList
+CREATE TYPE dbo.ReferenceSearchParamList AS TABLE
+(
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,SearchParamId smallint NOT NULL
+ ,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
+ ,ReferenceResourceTypeId smallint NULL
+ ,ReferenceResourceId varchar(768) COLLATE Latin1_General_100_CS_AS NOT NULL
+ ,ReferenceResourceVersion int NULL
+
+ UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceId)
+)
+ ')
+
+ EXECUTE('-- CaptureResourceIdsForChanges
+CREATE PROCEDURE dbo.CaptureResourceIdsForChanges @Resources dbo.ResourceList READONLY, @ResourcesLake dbo.ResourceListLake READONLY
+AS
+set nocount on
+-- This procedure is intended to be called from the MergeResources procedure and relies on its transaction logic
+INSERT INTO dbo.ResourceChangeData
+ ( ResourceId, ResourceTypeId, ResourceVersion, ResourceChangeTypeId )
+ SELECT ResourceId, ResourceTypeId, Version, CASE WHEN IsDeleted = 1 THEN 2 WHEN Version > 1 THEN 1 ELSE 0 END
+ FROM (SELECT ResourceId, ResourceTypeId, Version, IsHistory, IsDeleted FROM @Resources UNION ALL SELECT ResourceId, ResourceTypeId, Version, IsHistory, IsDeleted FROM @ResourcesLake) A
+ WHERE IsHistory = 0
+')
+
+ -- Special versions of procedures for data movement
+ EXECUTE('-- UpdateResourceSearchParams
+CREATE PROCEDURE dbo.UpdateResourceSearchParams
+ @FailedResources int = 0 OUT
+ ,@Resources dbo.ResourceList READONLY -- TODO: Remove after deployment
+ ,@ResourcesLake dbo.ResourceListLake READONLY
+ ,@ResourceWriteClaims dbo.ResourceWriteClaimList READONLY
+ ,@ReferenceSearchParams dbo.ReferenceSearchParamList READONLY
+ ,@TokenSearchParams dbo.TokenSearchParamList READONLY
+ ,@TokenTexts dbo.TokenTextList READONLY
+ ,@StringSearchParams dbo.StringSearchParamList READONLY
+ ,@UriSearchParams dbo.UriSearchParamList READONLY
+ ,@NumberSearchParams dbo.NumberSearchParamList READONLY
+ ,@QuantitySearchParams dbo.QuantitySearchParamList READONLY
+ ,@DateTimeSearchParams dbo.DateTimeSearchParamList READONLY
+ ,@ReferenceTokenCompositeSearchParams dbo.ReferenceTokenCompositeSearchParamList READONLY
+ ,@TokenTokenCompositeSearchParams dbo.TokenTokenCompositeSearchParamList READONLY
+ ,@TokenDateTimeCompositeSearchParams dbo.TokenDateTimeCompositeSearchParamList READONLY
+ ,@TokenQuantityCompositeSearchParams dbo.TokenQuantityCompositeSearchParamList READONLY
+ ,@TokenStringCompositeSearchParams dbo.TokenStringCompositeSearchParamList READONLY
+ ,@TokenNumberNumberCompositeSearchParams dbo.TokenNumberNumberCompositeSearchParamList READONLY
+AS
+set nocount on
+DECLARE @st datetime = getUTCdate()
+ ,@SP varchar(100) = object_name(@@procid)
+ ,@Mode varchar(200) = isnull((SELECT ''RT=[''+convert(varchar,min(ResourceTypeId))+'',''+convert(varchar,max(ResourceTypeId))+''] Sur=[''+convert(varchar,min(ResourceSurrogateId))+'',''+convert(varchar,max(ResourceSurrogateId))+''] V=''+convert(varchar,max(Version))+'' Rows=''+convert(varchar,count(*)) FROM (SELECT ResourceTypeId, ResourceSurrogateId, Version FROM @ResourcesLake UNION ALL SELECT ResourceTypeId, ResourceSurrogateId, Version FROM @Resources) A),''Input=Empty'')
+ ,@ResourceRows int
+ ,@InsertRows int
+ ,@DeletedIdMap int
+ ,@FirstIdInt bigint
+ ,@CurrentRows int
+
+RetryResourceIdIntMapLogic:
+BEGIN TRY
+ DECLARE @Ids TABLE (ResourceTypeId smallint NOT NULL, ResourceSurrogateId bigint NOT NULL)
+ DECLARE @CurrentRefIdsRaw TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL)
+ DECLARE @CurrentRefIds TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL PRIMARY KEY (ResourceTypeId, ResourceIdInt))
+ DECLARE @InputRefIds AS TABLE (ResourceTypeId smallint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ExistingRefIds AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertRefIds AS TABLE (ResourceTypeId smallint NOT NULL, IdIndex int NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertedRefIds AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ReferenceSearchParamsWithIds AS TABLE
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,SearchParamId smallint NOT NULL
+ ,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
+ ,ReferenceResourceTypeId smallint NULL
+ ,ReferenceResourceIdInt bigint NOT NULL
+ ,ReferenceResourceVersion int NULL
+
+ UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt)
+ )
+
+ -- Prepare insert into ResourceIdIntMap outside of transaction to minimize blocking
+ INSERT INTO @InputRefIds SELECT DISTINCT ReferenceResourceTypeId, ReferenceResourceId FROM @ReferenceSearchParams WHERE ReferenceResourceTypeId IS NOT NULL
+
+ INSERT INTO @ExistingRefIds
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT A.ResourceTypeId, ResourceIdInt, A.ResourceId
+ FROM @InputRefIds A
+ JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+
+ INSERT INTO @InsertRefIds
+ ( ResourceTypeId, IdIndex, ResourceId )
+ SELECT ResourceTypeId, row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1, ResourceId
+ FROM @InputRefIds A
+ WHERE NOT EXISTS (SELECT * FROM @ExistingRefIds B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId)
+
+ SET @InsertRows = (SELECT count(*) FROM @InsertRefIds)
+ IF @InsertRows > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @InsertRows, @FirstIdInt OUT
+
+ INSERT INTO @InsertedRefIds
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, IdIndex + @FirstIdInt, ResourceId
+ FROM @InsertRefIds
+ END
+
+ INSERT INTO @ReferenceSearchParamsWithIds
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt, ReferenceResourceVersion )
+ SELECT A.ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, isnull(C.ResourceIdInt,B.ResourceIdInt), ReferenceResourceVersion
+ FROM @ReferenceSearchParams A
+ LEFT OUTER JOIN @InsertedRefIds B ON B.ResourceTypeId = A.ReferenceResourceTypeId AND B.ResourceId = A.ReferenceResourceId
+ LEFT OUTER JOIN @ExistingRefIds C ON C.ResourceTypeId = A.ReferenceResourceTypeId AND C.ResourceId = A.ReferenceResourceId
+
+ BEGIN TRANSACTION
+
+ -- Update the search parameter hash value in the main resource table
+ IF EXISTS (SELECT * FROM @ResourcesLake)
+ UPDATE B
+ SET SearchParamHash = (SELECT SearchParamHash FROM @ResourcesLake A WHERE A.ResourceTypeId = B.ResourceTypeId AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ OUTPUT deleted.ResourceTypeId, deleted.ResourceSurrogateId INTO @Ids
+ FROM dbo.Resource B
+ WHERE EXISTS (SELECT * FROM @ResourcesLake A WHERE A.ResourceTypeId = B.ResourceTypeId AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ AND B.IsHistory = 0
+ ELSE
+ UPDATE B
+ SET SearchParamHash = (SELECT SearchParamHash FROM @Resources A WHERE A.ResourceTypeId = B.ResourceTypeId AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ OUTPUT deleted.ResourceTypeId, deleted.ResourceSurrogateId INTO @Ids
+ FROM dbo.Resource B
+ WHERE EXISTS (SELECT * FROM @Resources A WHERE A.ResourceTypeId = B.ResourceTypeId AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ AND B.IsHistory = 0
+ SET @ResourceRows = @@rowcount
+
+ -- First, delete all the search params of the resources to reindex.
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ResourceWriteClaim B ON B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ReferenceSearchParamTbl B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B
+ OUTPUT deleted.ReferenceResourceTypeId, deleted.ReferenceResourceIdInt INTO @CurrentRefIdsRaw
+ FROM @Ids A INNER LOOP JOIN dbo.ResourceReferenceSearchParams B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.StringReferenceSearchParams B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenText B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.StringSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.UriSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.NumberSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.QuantitySearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.DateTimeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ReferenceTokenCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenTokenCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenDateTimeCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenQuantityCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenStringCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenNumberNumberCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ -- Next, insert all the new search params.
+ INSERT INTO dbo.ResourceWriteClaim
+ ( ResourceSurrogateId, ClaimTypeId, ClaimValue )
+ SELECT ResourceSurrogateId, ClaimTypeId, ClaimValue
+ FROM @ResourceWriteClaims
+
+ -- start delete logic from ResourceIdIntMap
+ INSERT INTO @CurrentRefIds SELECT DISTINCT ResourceTypeId, ResourceIdInt FROM @CurrentRefIdsRaw
+ SET @CurrentRows = @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove not reused
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM @ReferenceSearchParamsWithIds B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove referenced by resources
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove referenced by reference search params
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- finally delete from id map
+ DELETE FROM B FROM @CurrentRefIds A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap = @@rowcount
+ END
+ END
+ END
+ END
+
+ INSERT INTO dbo.ResourceIdIntMap
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, ResourceIdInt, ResourceId
+ FROM @InsertedRefIds
+
+ INSERT INTO dbo.ResourceReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt
+ FROM @ReferenceSearchParamsWithIds
+
+ INSERT INTO dbo.StringReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId
+ FROM @ReferenceSearchParams
+ WHERE ReferenceResourceTypeId IS NULL
+
+ INSERT INTO dbo.TokenSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow
+ FROM @TokenSearchParams
+
+ INSERT INTO dbo.TokenText
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Text )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Text
+ FROM @TokenTexts
+
+ INSERT INTO dbo.StringSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax
+ FROM @StringSearchParams
+
+ INSERT INTO dbo.UriSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri
+ FROM @UriSearchParams
+
+ INSERT INTO dbo.NumberSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue
+ FROM @NumberSearchParams
+
+ INSERT INTO dbo.QuantitySearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue
+ FROM @QuantitySearchParams
+
+ INSERT INTO dbo.DateTimeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax
+ FROM @DateTimeSearchParams
+
+ INSERT INTO dbo.ReferenceTokenCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2
+ FROM @ReferenceTokenCompositeSearchParams
+
+ INSERT INTO dbo.TokenTokenCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2
+ FROM @TokenTokenCompositeSearchParams
+
+ INSERT INTO dbo.TokenDateTimeCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2
+ FROM @TokenDateTimeCompositeSearchParams
+
+ INSERT INTO dbo.TokenQuantityCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2
+ FROM @TokenQuantityCompositeSearchParams
+
+ INSERT INTO dbo.TokenStringCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2
+ FROM @TokenStringCompositeSearchParams
+
+ INSERT INTO dbo.TokenNumberNumberCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange
+ FROM @TokenNumberNumberCompositeSearchParams
+
+ COMMIT TRANSACTION
+
+ SET @FailedResources = (SELECT count(*) FROM @Resources) + (SELECT count(*) FROM @ResourcesLake) - @ResourceRows
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Start=@st,@Rows=@ResourceRows,@Text=@DeletedIdMap
+END TRY
+BEGIN CATCH
+ IF @@trancount > 0 ROLLBACK TRANSACTION
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error'',@Start=@st
+
+ IF error_number() IN (2601, 2627) AND error_message() LIKE ''%''''dbo.ResourceIdIntMap''''%'' -- pk violation
+ OR error_number() = 547 AND error_message() LIKE ''%DELETE%'' -- reference violation on DELETE
+ BEGIN
+ DELETE FROM @Ids
+ DELETE FROM @InputRefIds
+ DELETE FROM @CurrentRefIdsRaw
+ DELETE FROM @CurrentRefIds
+ DELETE FROM @ExistingRefIds
+ DELETE FROM @InsertRefIds
+ DELETE FROM @InsertedRefIds
+ DELETE FROM @ReferenceSearchParamsWithIds
+
+ GOTO RetryResourceIdIntMapLogic
+ END
+ ELSE
+ THROW
+END CATCH
+')
+
+ EXECUTE('-- MergeResources
+CREATE PROCEDURE dbo.MergeResources
+-- This stored procedure can be used for:
+-- 1. Ordinary put with single version per resource in input
+-- 2. Put with history preservation (multiple input versions per resource)
+-- 3. Copy from one gen2 store to another with ResourceSurrogateId preserved.
+ @AffectedRows int = 0 OUT
+ ,@RaiseExceptionOnConflict bit = 1
+ ,@IsResourceChangeCaptureEnabled bit = 0
+ ,@TransactionId bigint = NULL
+ ,@SingleTransaction bit = 1
+ ,@Resources dbo.ResourceList READONLY -- before lake code. TODO: Remove after deployment
+ ,@ResourcesLake dbo.ResourceListLake READONLY -- Lake code
+ ,@ResourceWriteClaims dbo.ResourceWriteClaimList READONLY
+ ,@ReferenceSearchParams dbo.ReferenceSearchParamList READONLY
+ ,@TokenSearchParams dbo.TokenSearchParamList READONLY
+ ,@TokenTexts dbo.TokenTextList READONLY
+ ,@StringSearchParams dbo.StringSearchParamList READONLY
+ ,@UriSearchParams dbo.UriSearchParamList READONLY
+ ,@NumberSearchParams dbo.NumberSearchParamList READONLY
+ ,@QuantitySearchParams dbo.QuantitySearchParamList READONLY
+ ,@DateTimeSearchParms dbo.DateTimeSearchParamList READONLY
+ ,@ReferenceTokenCompositeSearchParams dbo.ReferenceTokenCompositeSearchParamList READONLY
+ ,@TokenTokenCompositeSearchParams dbo.TokenTokenCompositeSearchParamList READONLY
+ ,@TokenDateTimeCompositeSearchParams dbo.TokenDateTimeCompositeSearchParamList READONLY
+ ,@TokenQuantityCompositeSearchParams dbo.TokenQuantityCompositeSearchParamList READONLY
+ ,@TokenStringCompositeSearchParams dbo.TokenStringCompositeSearchParamList READONLY
+ ,@TokenNumberNumberCompositeSearchParams dbo.TokenNumberNumberCompositeSearchParamList READONLY
+AS
+set nocount on
+DECLARE @st datetime = getUTCdate()
+ ,@SP varchar(100) = object_name(@@procid)
+ ,@DummyTop bigint = 9223372036854775807
+ ,@InitialTranCount int = @@trancount
+ ,@IsRetry bit = 0
+ ,@RT smallint
+ ,@NewIdsCount int
+ ,@FirstIdInt bigint
+ ,@CurrentRows int
+ ,@DeletedIdMap int
+
+DECLARE @Mode varchar(200) = isnull((SELECT ''RT=[''+convert(varchar,min(ResourceTypeId))+'',''+convert(varchar,max(ResourceTypeId))+''] Sur=[''+convert(varchar,min(ResourceSurrogateId))+'',''+convert(varchar,max(ResourceSurrogateId))+''] V=''+convert(varchar,max(Version))+'' Rows=''+convert(varchar,count(*)) FROM (SELECT ResourceTypeId, ResourceSurrogateId, Version FROM @Resources UNION ALL SELECT ResourceTypeId, ResourceSurrogateId, Version FROM @ResourcesLake) A),''Input=Empty'')
+SET @Mode += '' E=''+convert(varchar,@RaiseExceptionOnConflict)+'' CC=''+convert(varchar,@IsResourceChangeCaptureEnabled)+'' IT=''+convert(varchar,@InitialTranCount)+'' T=''+isnull(convert(varchar,@TransactionId),''NULL'')
+
+SET @AffectedRows = 0
+
+RetryResourceIdIntMapLogic:
+BEGIN TRY
+ DECLARE @InputIds AS TABLE (ResourceTypeId smallint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @CurrentRefIdsRaw TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL)
+ DECLARE @CurrentRefIds TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL PRIMARY KEY (ResourceTypeId, ResourceIdInt))
+ DECLARE @ExistingIdsReference AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ExistingIdsResource AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertIds AS TABLE (ResourceTypeId smallint NOT NULL, IdIndex int NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertedIdsReference AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertedIdsResource AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ResourcesWithIds AS TABLE
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL
+ ,ResourceIdInt bigint NOT NULL
+ ,Version int NOT NULL
+ ,HasVersionToCompare bit NOT NULL -- in case of multiple versions per resource indicates that row contains (existing version + 1) value
+ ,IsDeleted bit NOT NULL
+ ,IsHistory bit NOT NULL
+ ,KeepHistory bit NOT NULL
+ ,RawResource varbinary(max) NULL
+ ,IsRawResourceMetaSet bit NOT NULL
+ ,RequestMethod varchar(10) NULL
+ ,SearchParamHash varchar(64) NULL
+ ,FileId bigint NULL
+ ,OffsetInFile int NULL
+
+ PRIMARY KEY (ResourceTypeId, ResourceSurrogateId)
+ ,UNIQUE (ResourceTypeId, ResourceIdInt, Version)
+ )
+ DECLARE @ReferenceSearchParamsWithIds AS TABLE
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,SearchParamId smallint NOT NULL
+ ,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
+ ,ReferenceResourceTypeId smallint NOT NULL
+ ,ReferenceResourceIdInt bigint NOT NULL
+
+ UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt)
+ )
+
+ -- Prepare id map for reference search params Start ---------------------------------------------------------------------------
+ INSERT INTO @InputIds SELECT DISTINCT ReferenceResourceTypeId, ReferenceResourceId FROM @ReferenceSearchParams WHERE ReferenceResourceTypeId IS NOT NULL
+
+ INSERT INTO @ExistingIdsReference
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT A.ResourceTypeId, ResourceIdInt, A.ResourceId
+ FROM @InputIds A
+ JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+
+ INSERT INTO @InsertIds
+ ( ResourceTypeId, IdIndex, ResourceId )
+ SELECT ResourceTypeId, row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1, ResourceId
+ FROM @InputIds A
+ WHERE NOT EXISTS (SELECT * FROM @ExistingIdsReference B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId)
+
+ SET @NewIdsCount = (SELECT count(*) FROM @InsertIds)
+ IF @NewIdsCount > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @NewIdsCount, @FirstIdInt OUT
+
+ INSERT INTO @InsertedIdsReference
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, IdIndex + @FirstIdInt, ResourceId
+ FROM @InsertIds
+ END
+
+ INSERT INTO @ReferenceSearchParamsWithIds
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT A.ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, isnull(C.ResourceIdInt,B.ResourceIdInt)
+ FROM @ReferenceSearchParams A
+ LEFT OUTER JOIN @InsertedIdsReference B ON B.ResourceTypeId = A.ReferenceResourceTypeId AND B.ResourceId = A.ReferenceResourceId
+ LEFT OUTER JOIN @ExistingIdsReference C ON C.ResourceTypeId = A.ReferenceResourceTypeId AND C.ResourceId = A.ReferenceResourceId
+ WHERE ReferenceResourceTypeId IS NOT NULL
+ -- Prepare id map for reference search params End ---------------------------------------------------------------------------
+
+ -- Prepare id map for resources Start ---------------------------------------------------------------------------
+ DELETE FROM @InputIds
+ IF EXISTS (SELECT * FROM @ResourcesLake)
+ INSERT INTO @InputIds SELECT ResourceTypeId, ResourceId FROM @ResourcesLake GROUP BY ResourceTypeId, ResourceId
+ ELSE
+ INSERT INTO @InputIds SELECT ResourceTypeId, ResourceId FROM @Resources GROUP BY ResourceTypeId, ResourceId
+
+ INSERT INTO @ExistingIdsResource
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT A.ResourceTypeId, isnull(C.ResourceIdInt,B.ResourceIdInt), A.ResourceId
+ FROM @InputIds A
+ LEFT OUTER JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN @InsertedIdsReference C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceId = A.ResourceId
+ WHERE C.ResourceIdInt IS NOT NULL OR B.ResourceIdInt IS NOT NULL
+
+ DELETE FROM @InsertIds
+ INSERT INTO @InsertIds
+ ( ResourceTypeId, IdIndex, ResourceId )
+ SELECT ResourceTypeId, row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1, ResourceId
+ FROM @InputIds A
+ WHERE NOT EXISTS (SELECT * FROM @ExistingIdsResource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId)
+
+ SET @NewIdsCount = (SELECT count(*) FROM @InsertIds)
+ IF @NewIdsCount > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @NewIdsCount, @FirstIdInt OUT
+
+ INSERT INTO @InsertedIdsResource
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, IdIndex + @FirstIdInt, ResourceId
+ FROM @InsertIds
+ END
+
+ IF EXISTS (SELECT * FROM @ResourcesLake)
+ INSERT INTO @ResourcesWithIds
+ ( ResourceTypeId, ResourceId, ResourceIdInt, Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, FileId, OffsetInFile )
+ SELECT A.ResourceTypeId, A.ResourceId, isnull(C.ResourceIdInt,B.ResourceIdInt), Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, FileId, OffsetInFile
+ FROM @ResourcesLake A
+ LEFT OUTER JOIN @InsertedIdsResource B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN @ExistingIdsResource C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceId = A.ResourceId
+ ELSE
+ INSERT INTO @ResourcesWithIds
+ ( ResourceTypeId, ResourceId, ResourceIdInt, Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, FileId, OffsetInFile )
+ SELECT A.ResourceTypeId, A.ResourceId, isnull(C.ResourceIdInt,B.ResourceIdInt), Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, NULL, NULL
+ FROM @Resources A
+ LEFT OUTER JOIN @InsertedIdsResource B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN @ExistingIdsResource C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceId = A.ResourceId
+ -- Prepare id map for resources End ---------------------------------------------------------------------------
+
+ DECLARE @Existing AS TABLE (ResourceTypeId smallint NOT NULL, SurrogateId bigint NOT NULL PRIMARY KEY (ResourceTypeId, SurrogateId))
+
+ DECLARE @ResourceInfos AS TABLE
+ (
+ ResourceTypeId smallint NOT NULL
+ ,SurrogateId bigint NOT NULL
+ ,Version int NOT NULL
+ ,KeepHistory bit NOT NULL
+ ,PreviousVersion int NULL
+ ,PreviousSurrogateId bigint NULL
+
+ PRIMARY KEY (ResourceTypeId, SurrogateId)
+ )
+
+ DECLARE @PreviousSurrogateIds AS TABLE (TypeId smallint NOT NULL, SurrogateId bigint NOT NULL PRIMARY KEY (TypeId, SurrogateId), KeepHistory bit)
+
+ IF @SingleTransaction = 0 AND isnull((SELECT Number FROM dbo.Parameters WHERE Id = ''MergeResources.NoTransaction.IsEnabled''),0) = 0
+ SET @SingleTransaction = 1
+
+ SET @Mode += '' ST=''+convert(varchar,@SingleTransaction)
+
+ -- perform retry check in transaction to hold locks
+ IF @InitialTranCount = 0
+ BEGIN
+ IF EXISTS (SELECT * -- This extra statement avoids putting range locks when we don''t need them
+ FROM @ResourcesWithIds A JOIN dbo.Resource B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ WHERE B.IsHistory = 0
+ )
+ BEGIN
+ BEGIN TRANSACTION
+
+ INSERT INTO @Existing
+ ( ResourceTypeId, SurrogateId )
+ SELECT B.ResourceTypeId, B.ResourceSurrogateId
+ FROM (SELECT TOP (@DummyTop) * FROM @ResourcesWithIds) A
+ JOIN dbo.Resource B WITH (ROWLOCK, HOLDLOCK) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ WHERE B.IsHistory = 0
+ AND B.ResourceId = A.ResourceId
+ AND B.Version = A.Version
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+
+ IF @@rowcount = (SELECT count(*) FROM @ResourcesWithIds) SET @IsRetry = 1
+
+ IF @IsRetry = 0 COMMIT TRANSACTION -- commit check transaction
+ END
+ END
+
+ SET @Mode += '' R=''+convert(varchar,@IsRetry)
+
+ IF @SingleTransaction = 1 AND @@trancount = 0 BEGIN TRANSACTION
+
+ IF @IsRetry = 0
+ BEGIN
+ INSERT INTO @ResourceInfos
+ ( ResourceTypeId, SurrogateId, Version, KeepHistory, PreviousVersion, PreviousSurrogateId )
+ SELECT A.ResourceTypeId, A.ResourceSurrogateId, A.Version, A.KeepHistory, B.Version, B.ResourceSurrogateId
+ FROM (SELECT TOP (@DummyTop) * FROM @ResourcesWithIds WHERE HasVersionToCompare = 1) A
+ LEFT OUTER JOIN dbo.CurrentResource B -- WITH (UPDLOCK, HOLDLOCK) These locking hints cause deadlocks and are not needed. Racing might lead to tries to insert dups in unique index (with version key), but it will fail anyway, and in no case this will cause incorrect data saved.
+ ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+
+ IF @RaiseExceptionOnConflict = 1 AND EXISTS (SELECT * FROM @ResourceInfos WHERE PreviousVersion IS NOT NULL AND Version <= PreviousVersion)
+ THROW 50409, ''Resource has been recently updated or added, please compare the resource content in code for any duplicate updates'', 1
+
+ INSERT INTO @PreviousSurrogateIds
+ SELECT ResourceTypeId, PreviousSurrogateId, KeepHistory
+ FROM @ResourceInfos
+ WHERE PreviousSurrogateId IS NOT NULL
+
+ IF @@rowcount > 0
+ BEGIN
+ UPDATE dbo.Resource
+ SET IsHistory = 1
+ WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId AND KeepHistory = 1)
+ SET @AffectedRows += @@rowcount
+
+ IF @IsResourceChangeCaptureEnabled = 1 AND NOT EXISTS (SELECT * FROM dbo.Parameters WHERE Id = ''InvisibleHistory.IsEnabled'' AND Number = 0)
+ UPDATE dbo.Resource
+ SET IsHistory = 1
+ ,RawResource = 0xF -- "invisible" value
+ ,SearchParamHash = NULL
+ ,HistoryTransactionId = @TransactionId
+ WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId AND KeepHistory = 0)
+ ELSE
+ DELETE FROM dbo.Resource WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId AND KeepHistory = 0)
+ SET @AffectedRows += @@rowcount
+
+ DELETE FROM dbo.ResourceWriteClaim WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.ReferenceSearchParamTbl WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.ResourceReferenceSearchParams
+ OUTPUT deleted.ReferenceResourceTypeId, deleted.ReferenceResourceIdInt INTO @CurrentRefIdsRaw
+ WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @CurrentRows = @@rowcount
+ SET @AffectedRows += @CurrentRows
+ -- start deleting from ResourceIdIntMap
+ INSERT INTO @CurrentRefIds SELECT DISTINCT ResourceTypeId, ResourceIdInt FROM @CurrentRefIdsRaw
+ SET @CurrentRows = @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove not reused
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM @ReferenceSearchParamsWithIds B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove referenced in Resources
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove still referenced in ResourceReferenceSearchParams
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- delete from id map
+ DELETE FROM B FROM @CurrentRefIds A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap = @@rowcount
+ END
+ END
+ END
+ END
+ DELETE FROM dbo.StringReferenceSearchParams WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenText WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.StringSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.UriSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.NumberSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.QuantitySearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.DateTimeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.ReferenceTokenCompositeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenTokenCompositeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenDateTimeCompositeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenQuantityCompositeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenStringCompositeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenNumberNumberCompositeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+
+ --EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Info'',@Start=@st,@Rows=@AffectedRows,@Text=''Old rows''
+ END
+
+ INSERT INTO dbo.ResourceIdIntMap
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, ResourceIdInt, ResourceId
+ FROM @InsertedIdsResource
+
+ INSERT INTO dbo.ResourceIdIntMap
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, ResourceIdInt, ResourceId
+ FROM @InsertedIdsReference
+
+ INSERT INTO dbo.Resource
+ ( ResourceTypeId, ResourceIdInt, Version, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, RawResource, IsRawResourceMetaSet, SearchParamHash, TransactionId, FileId, OffsetInFile )
+ SELECT ResourceTypeId, ResourceIdInt, Version, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, RawResource, IsRawResourceMetaSet, SearchParamHash, @TransactionId, FileId, OffsetInFile
+ FROM @ResourcesWithIds
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.ResourceWriteClaim
+ ( ResourceSurrogateId, ClaimTypeId, ClaimValue )
+ SELECT ResourceSurrogateId, ClaimTypeId, ClaimValue
+ FROM @ResourceWriteClaims
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.ResourceReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt
+ FROM @ReferenceSearchParamsWithIds
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.StringReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId
+ FROM @ReferenceSearchParams
+ WHERE ReferenceResourceTypeId IS NULL
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow
+ FROM @TokenSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenText
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Text )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Text
+ FROM @TokenTexts
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.StringSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax
+ FROM @StringSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.UriSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri
+ FROM @UriSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.NumberSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue
+ FROM @NumberSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.QuantitySearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue
+ FROM @QuantitySearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.DateTimeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax
+ FROM @DateTimeSearchParms
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.ReferenceTokenCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2
+ FROM @ReferenceTokenCompositeSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenTokenCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2
+ FROM @TokenTokenCompositeSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenDateTimeCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2
+ FROM @TokenDateTimeCompositeSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenQuantityCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2
+ FROM @TokenQuantityCompositeSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenStringCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2
+ FROM @TokenStringCompositeSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenNumberNumberCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange
+ FROM @TokenNumberNumberCompositeSearchParams
+ SET @AffectedRows += @@rowcount
+ END -- @IsRetry = 0
+ ELSE
+ BEGIN -- @IsRetry = 1
+ INSERT INTO dbo.ResourceWriteClaim
+ ( ResourceSurrogateId, ClaimTypeId, ClaimValue )
+ SELECT ResourceSurrogateId, ClaimTypeId, ClaimValue
+ FROM (SELECT TOP (@DummyTop) * FROM @ResourceWriteClaims) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.ResourceWriteClaim C WHERE C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.ResourceReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT A.ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt
+ FROM (SELECT TOP (@DummyTop) * FROM @ReferenceSearchParamsWithIds) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.StringReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId )
+ SELECT A.ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId
+ FROM (SELECT TOP (@DummyTop) * FROM @ReferenceSearchParams WHERE ReferenceResourceTypeId IS NULL) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.StringReferenceSearchParams C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenText
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Text )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Text
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenTexts) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.StringSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax
+ FROM (SELECT TOP (@DummyTop) * FROM @StringSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenText C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.UriSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri
+ FROM (SELECT TOP (@DummyTop) * FROM @UriSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.UriSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.NumberSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue
+ FROM (SELECT TOP (@DummyTop) * FROM @NumberSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.NumberSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.QuantitySearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue
+ FROM (SELECT TOP (@DummyTop) * FROM @QuantitySearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.QuantitySearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.DateTimeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax
+ FROM (SELECT TOP (@DummyTop) * FROM @DateTimeSearchParms) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.ReferenceTokenCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2
+ FROM (SELECT TOP (@DummyTop) * FROM @ReferenceTokenCompositeSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.DateTimeSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenTokenCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenTokenCompositeSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenTokenCompositeSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenDateTimeCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenDateTimeCompositeSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenDateTimeCompositeSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenQuantityCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenQuantityCompositeSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenQuantityCompositeSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenStringCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenStringCompositeSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenStringCompositeSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenNumberNumberCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenNumberNumberCompositeSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenNumberNumberCompositeSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+ END
+
+ IF @IsResourceChangeCaptureEnabled = 1 --If the resource change capture feature is enabled, to execute a stored procedure called CaptureResourceChanges to insert resource change data.
+ EXECUTE dbo.CaptureResourceIdsForChanges @Resources, @ResourcesLake
+
+ IF @TransactionId IS NOT NULL
+ EXECUTE dbo.MergeResourcesCommitTransaction @TransactionId
+
+ IF @InitialTranCount = 0 AND @@trancount > 0 COMMIT TRANSACTION
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Start=@st,@Rows=@AffectedRows,@Text=@DeletedIdMap
+END TRY
+BEGIN CATCH
+ IF @InitialTranCount = 0 AND @@trancount > 0 ROLLBACK TRANSACTION
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error'',@Start=@st
+
+ IF error_number() IN (2601, 2627) AND error_message() LIKE ''%''''dbo.ResourceIdIntMap''''%'' -- pk violation
+ OR error_number() = 547 AND error_message() LIKE ''%DELETE%'' -- reference violation on DELETE
+ BEGIN
+ DELETE FROM @ResourcesWithIds
+ DELETE FROM @ReferenceSearchParamsWithIds
+ DELETE FROM @CurrentRefIdsRaw
+ DELETE FROM @CurrentRefIds
+ DELETE FROM @InputIds
+ DELETE FROM @InsertIds
+ DELETE FROM @InsertedIdsReference
+ DELETE FROM @ExistingIdsReference
+ DELETE FROM @InsertedIdsResource
+ DELETE FROM @ExistingIdsResource
+ DELETE FROM @Existing
+ DELETE FROM @ResourceInfos
+ DELETE FROM @PreviousSurrogateIds
+
+ GOTO RetryResourceIdIntMapLogic
+ END
+ ELSE
+ IF @RaiseExceptionOnConflict = 1 AND error_number() IN (2601, 2627) AND (error_message() LIKE ''%''''dbo.Resource%'' OR error_message() LIKE ''%''''dbo.CurrentResources%'' OR error_message() LIKE ''%''''dbo.HistoryResources%'' OR error_message() LIKE ''%''''dbo.RawResources''''%'')
+ THROW 50409, ''Resource has been recently updated or added, please compare the resource content in code for any duplicate updates'', 1;
+ ELSE
+ THROW
+END CATCH
+ ')
+
+ EXECUTE('-- GetResourcesByTypeAndSurrogateIdRange
+ALTER PROCEDURE dbo.GetResourcesByTypeAndSurrogateIdRange @ResourceTypeId smallint, @StartId bigint, @EndId bigint, @GlobalEndId bigint = NULL, @IncludeHistory bit = 1, @IncludeDeleted bit = 1
+AS
+set nocount on
+DECLARE @SP varchar(100) = ''GetResourcesByTypeAndSurrogateIdRange''
+ ,@Mode varchar(100) = ''RT=''+isnull(convert(varchar,@ResourceTypeId),''NULL'')
+ +'' S=''+isnull(convert(varchar,@StartId),''NULL'')
+ +'' E=''+isnull(convert(varchar,@EndId),''NULL'')
+ +'' GE=''+isnull(convert(varchar,@GlobalEndId),''NULL'')
+ +'' HI=''+isnull(convert(varchar,@IncludeHistory),''NULL'')
+ +'' DE=''+isnull(convert(varchar,@IncludeDeleted),''NULL'')
+ ,@st datetime = getUTCdate()
+ ,@DummyTop bigint = 9223372036854775807
+ ,@Rows int
+
+BEGIN TRY
+ DECLARE @ResourceIds TABLE (ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS PRIMARY KEY)
+ DECLARE @SurrogateIds TABLE (MaxSurrogateId bigint PRIMARY KEY)
+
+ IF @GlobalEndId IS NOT NULL AND @IncludeHistory = 0 -- snapshot view
+ BEGIN
+ INSERT INTO @ResourceIds
+ SELECT DISTINCT ResourceId
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ AND IsHistory = 1
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ OPTION (MAXDOP 1)
+
+ IF @@rowcount > 0
+ INSERT INTO @SurrogateIds
+ SELECT ResourceSurrogateId
+ FROM (SELECT ResourceId, ResourceSurrogateId, RowId = row_number() OVER (PARTITION BY ResourceId ORDER BY ResourceSurrogateId DESC)
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceId IN (SELECT TOP (@DummyTop) ResourceId FROM @ResourceIds)
+ AND ResourceSurrogateId BETWEEN @StartId AND @GlobalEndId
+ ) A
+ WHERE RowId = 1
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ END
+
+ IF @IncludeHistory = 0
+ SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource, FileId, OffsetInFile
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ AND IsHistory = 0
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ UNION ALL
+ SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource, FileId, OffsetInFile
+ FROM @SurrogateIds
+ JOIN dbo.Resource ON ResourceTypeId = @ResourceTypeId AND ResourceSurrogateId = MaxSurrogateId
+ WHERE IsHistory = 1
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ OPTION (MAXDOP 1, LOOP JOIN)
+ ELSE -- @IncludeHistory = 1
+ SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource, FileId, OffsetInFile
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ UNION ALL
+ SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource, FileId, OffsetInFile
+ FROM @SurrogateIds
+ JOIN dbo.Resource ON ResourceTypeId = @ResourceTypeId AND ResourceSurrogateId = MaxSurrogateId
+ WHERE IsHistory = 1
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ OPTION (MAXDOP 1, LOOP JOIN)
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Start=@st,@Rows=@@rowcount
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error'';
+ THROW
+END CATCH
+ ')
+
+ EXECUTE('-- MergeResourcesDeleteInvisibleHistory
+ALTER PROCEDURE dbo.MergeResourcesDeleteInvisibleHistory @TransactionId bigint, @AffectedRows int = NULL OUT
+AS
+set nocount on
+DECLARE @SP varchar(100) = object_name(@@procid)
+ ,@Mode varchar(100) = ''T=''+convert(varchar,@TransactionId)
+ ,@st datetime
+ ,@Rows int
+ ,@DeletedIdMap int
+ ,@TypeId smallint
+
+SET @AffectedRows = 0
+
+DECLARE @Types TABLE (TypeId smallint PRIMARY KEY, Name varchar(100))
+INSERT INTO @Types EXECUTE dbo.GetUsedResourceTypes
+
+DECLARE @Ids TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL)
+
+Retry:
+BEGIN TRY
+ WHILE EXISTS (SELECT * FROM @Types)
+ BEGIN
+ SET @TypeId = (SELECT TOP 1 TypeId FROM @Types ORDER BY TypeId)
+
+ DELETE FROM dbo.ResourceTbl WHERE ResourceTypeId = @TypeId AND HistoryTransactionId = @TransactionId AND RawResource = 0xF
+ SET @AffectedRows += @@rowcount
+
+ DELETE FROM @Types WHERE TypeId = @TypeId
+ END
+
+ BEGIN TRANSACTION
+
+ SET @st = getUTCdate()
+ DELETE FROM A
+ OUTPUT deleted.ResourceTypeId, deleted.ResourceIdInt INTO @Ids
+ FROM dbo.Resource A
+ WHERE HistoryTransactionId = @TransactionId -- requires statistics to reflect not null values
+ SET @Rows = @@rowcount
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Run'',@Target=''Resource'',@Action=''Delete'',@Start=@st,@Rows=@Rows
+ SET @AffectedRows += @Rows
+
+ SET @st = getUTCdate()
+ IF @Rows > 0
+ BEGIN
+ -- remove referenced in resources
+ DELETE FROM A FROM @Ids A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ -- remove referenced in reference search params
+ DELETE FROM A FROM @Ids A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ -- delete from id map
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap = @@rowcount
+ END
+ END
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Run'',@Target=''ResourceIdIntMap'',@Action=''Delete'',@Start=@st,@Rows=@DeletedIdMap
+ END
+
+ COMMIT TRANSACTION
+
+ SET @st = getUTCdate()
+ UPDATE dbo.Resource SET TransactionId = NULL WHERE TransactionId = @TransactionId
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Target=''Resource'',@Action=''Update'',@Start=@st,@Rows=@@rowcount
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+ IF @@trancount > 0 ROLLBACK TRANSACTION
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error''
+ IF error_number() = 547 AND error_message() LIKE ''%DELETE%'' -- reference violation on DELETE
+ BEGIN
+ DELETE FROM @Ids
+ GOTO Retry
+ END
+ ELSE
+ THROW
+END CATCH
+ ')
+
+ EXECUTE('-- GetResourcesByTransactionId
+ALTER PROCEDURE dbo.GetResourcesByTransactionId @TransactionId bigint, @IncludeHistory bit = 0, @ReturnResourceKeysOnly bit = 0
+AS
+set nocount on
+DECLARE @SP varchar(100) = object_name(@@procid)
+ ,@Mode varchar(100) = ''T=''+convert(varchar,@TransactionId)+'' H=''+convert(varchar,@IncludeHistory)
+ ,@st datetime = getUTCdate()
+
+BEGIN TRY
+ IF @ReturnResourceKeysOnly = 0
+ SELECT ResourceTypeId
+ ,ResourceId
+ ,ResourceSurrogateId
+ ,Version
+ ,IsDeleted
+ ,IsHistory
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,RequestMethod
+ ,FileId
+ ,OffsetInFile
+ FROM dbo.Resource
+ WHERE TransactionId = @TransactionId AND (IsHistory = 0 OR @IncludeHistory = 1)
+ OPTION (MAXDOP 1)
+ ELSE
+ SELECT ResourceTypeId
+ ,ResourceId
+ ,ResourceSurrogateId
+ ,Version
+ ,IsDeleted
+ FROM dbo.Resource
+ WHERE TransactionId = @TransactionId AND (IsHistory = 0 OR @IncludeHistory = 1)
+ OPTION (MAXDOP 1)
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Start=@st,@Rows=@@rowcount
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error'';
+ THROW
+END CATCH
+ ')
+
+ EXECUTE('-- GetResourceVersions
+ALTER PROCEDURE dbo.GetResourceVersions @ResourceDateKeys dbo.ResourceDateKeyList READONLY
+AS
+-- This stored procedure allows to identifiy if version gap is available and checks dups on lastUpdated
+set nocount on
+DECLARE @st datetime = getUTCdate()
+ ,@SP varchar(100) = ''GetResourceVersions''
+ ,@Mode varchar(100) = ''Rows=''+convert(varchar,(SELECT count(*) FROM @ResourceDateKeys))
+ ,@DummyTop bigint = 9223372036854775807
+
+BEGIN TRY
+ SELECT A.ResourceTypeId
+ ,A.ResourceId
+ ,A.ResourceSurrogateId
+ -- set version to 0 if there is no gap available, or lastUpdated is already used. It would indicate potential conflict for the caller.
+ ,Version = CASE
+ -- ResourceSurrogateId is generated from lastUpdated only without extra bits at the end. Need to ckeck interval (0..79999) on resource id level.
+ WHEN D.Version IS NOT NULL THEN 0 -- input lastUpdated matches stored
+ WHEN isnull(U.Version, 1) - isnull(L.Version, 0) > ResourceIndex THEN isnull(U.Version, 1) - ResourceIndex -- gap is available
+ ELSE isnull(M.Version, 0) - ResourceIndex -- late arrival
+ END
+ ,MatchedVersion = isnull(D.Version,0)
+ ,MatchedRawResource = D.RawResource
+ ,MatchedFileId = D.FileId
+ ,MatchedOffsetInFile = D.OffsetInFile
+ -- ResourceIndex allows to deal with more than one late arrival per resource
+ FROM (SELECT TOP (@DummyTop) A.*, ResourceIndex = convert(int,row_number() OVER (PARTITION BY A.ResourceTypeId, A.ResourceId ORDER BY ResourceSurrogateId DESC))
+ FROM @ResourceDateKeys A
+ ) A
+ OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.Version > 0 AND B.ResourceSurrogateId < A.ResourceSurrogateId ORDER BY B.ResourceSurrogateId DESC) L -- lower
+ OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.Version > 0 AND B.ResourceSurrogateId > A.ResourceSurrogateId ORDER BY B.ResourceSurrogateId) U -- upper
+ OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.Version < 0 ORDER BY B.Version) M -- minus
+ OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.ResourceSurrogateId BETWEEN A.ResourceSurrogateId AND A.ResourceSurrogateId + 79999) D -- date
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Start=@st,@Rows=@@rowcount
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error'',@Start=@st;
+ THROW
+END CATCH
+ ')
+
+ EXECUTE('-- HardDeleteResource
+ALTER PROCEDURE dbo.HardDeleteResource
+ @ResourceTypeId smallint
+ ,@ResourceId varchar(64)
+ ,@KeepCurrentVersion bit
+ ,@IsResourceChangeCaptureEnabled bit = 0 -- TODO: Remove input parameter after deployment
+ ,@MakeResourceInvisible bit = 0
+AS
+set nocount on
+DECLARE @SP varchar(100) = object_name(@@procid)
+ ,@Mode varchar(200) = ''RT=''+convert(varchar,@ResourceTypeId)+'' R=''+@ResourceId+'' V=''+convert(varchar,@KeepCurrentVersion)
+ ,@st datetime = getUTCdate()
+ ,@TransactionId bigint
+ ,@DeletedIdMap int = 0
+ ,@Rows int
+
+IF @IsResourceChangeCaptureEnabled = 1
+ SET @MakeResourceInvisible = 1
+
+SET @Mode += '' I=''+convert(varchar,@MakeResourceInvisible)
+
+IF @MakeResourceInvisible = 1
+BEGIN
+ EXECUTE dbo.MergeResourcesBeginTransaction @Count = 1, @TransactionId = @TransactionId OUT
+ SET @Mode += '' T=''+convert(varchar,@TransactionId)
+END
+
+DECLARE @Ids TABLE (ResourceSurrogateId bigint NOT NULL, ResourceIdInt bigint NULL)
+DECLARE @IdsDistinct TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL PRIMARY KEY (ResourceTypeId, ResourceIdInt))
+DECLARE @RefIdsRaw TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL)
+
+RetryResourceIdIntMapLogic:
+BEGIN TRY
+ BEGIN TRANSACTION
+
+ IF @MakeResourceInvisible = 1
+ UPDATE dbo.Resource
+ SET IsDeleted = 1
+ ,RawResource = 0xF -- invisible value
+ ,SearchParamHash = NULL
+ ,HistoryTransactionId = @TransactionId
+ OUTPUT deleted.ResourceSurrogateId, deleted.ResourceIdInt INTO @Ids
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceId = @ResourceId
+ AND (@KeepCurrentVersion = 0 OR IsHistory = 1)
+ AND (RawResource IS NULL -- stored in ADLS
+ OR RawResource <> 0xF -- stored in the database and not already invisible
+ )
+ ELSE
+ BEGIN
+ DELETE dbo.Resource
+ OUTPUT deleted.ResourceSurrogateId, deleted.ResourceIdInt INTO @Ids
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceId = @ResourceId
+ AND (@KeepCurrentVersion = 0 OR IsHistory = 1)
+ AND RawResource <> 0xF
+
+ INSERT INTO @IdsDistinct SELECT DISTINCT @ResourceTypeId, ResourceIdInt FROM @Ids WHERE ResourceIdInt IS NOT NULL
+ SET @Rows = @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM A FROM @IdsDistinct A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM A FROM @IdsDistinct A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM B FROM @IdsDistinct A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap = @@rowcount
+ END
+ END
+ END
+ END
+
+ IF @KeepCurrentVersion = 0
+ BEGIN
+ -- PAGLOCK allows deallocation of empty page without waiting for ghost cleanup
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ResourceWriteClaim B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ReferenceSearchParamTbl B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B
+ OUTPUT deleted.ReferenceResourceTypeId, deleted.ReferenceResourceIdInt INTO @RefIdsRaw
+ FROM @Ids A INNER LOOP JOIN dbo.ResourceReferenceSearchParams B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM @IdsDistinct -- is used above
+ INSERT INTO @IdsDistinct SELECT DISTINCT ResourceTypeId, ResourceIdInt FROM @RefIdsRaw
+ SET @Rows = @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM A FROM @IdsDistinct A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM A FROM @IdsDistinct A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM B FROM @IdsDistinct A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap += @@rowcount
+ END
+ END
+ END
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.StringReferenceSearchParams B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenText B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.StringSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.UriSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.NumberSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.QuantitySearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.DateTimeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ReferenceTokenCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenTokenCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenDateTimeCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenQuantityCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenStringCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenNumberNumberCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ END
+
+ COMMIT TRANSACTION
+
+ IF @MakeResourceInvisible = 1
+ EXECUTE dbo.MergeResourcesCommitTransaction @TransactionId
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Start=@st,@Text=@DeletedIdMap
+END TRY
+BEGIN CATCH
+ IF @@trancount > 0 ROLLBACK TRANSACTION
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error'',@Start=@st
+
+ IF error_number() = 547 AND error_message() LIKE ''%DELETE%''-- reference violation on DELETE
+ BEGIN
+ DELETE FROM @Ids
+ DELETE FROM @RefIdsRaw
+ DELETE FROM @IdsDistinct
+ GOTO RetryResourceIdIntMapLogic
+ END
+ ELSE
+ THROW
+END CATCH
+ ')
+
+ COMMIT TRANSACTION
+ END TRY
+ BEGIN CATCH
+ IF @@trancount > 0 ROLLBACK TRANSACTION
+ THROW
+ END CATCH
+END
+GO
+CREATE OR ALTER PROCEDURE dbo.tmp_MoveResources @ResourceTypeId smallint, @SurrogateId bigint, @CurrentMaxSurrogateId bigint, @LastProcessed varchar(100)
+AS
+set nocount on
+DECLARE @Process varchar(100) = 'LakeSchemaUpgrade.MoveResources'
+ ,@Id varchar(100) = 'LakeSchemaUpgrade.MoveResources.LastProcessed.TypeId.SurrogateId' -- SELECT * FROM Parameters
+ ,@st datetime
+ ,@NewIdsCount int
+ ,@FirstIdInt bigint
+ ,@DummyTop bigint = 9223372036854775807
+
+RetryResourceIdIntMapLogic:
+BEGIN TRY
+ DECLARE @InputIds AS TABLE (ResourceTypeId smallint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ExistingIdsReference AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ExistingIdsResource AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertIds AS TABLE (ResourceTypeId smallint NOT NULL, IdIndex int NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertedIdsReference AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertedIdsResource AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ResourcesWithIds AS TABLE
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL
+ ,ResourceIdInt bigint NOT NULL
+ ,Version int NOT NULL
+ ,IsDeleted bit NOT NULL
+ ,IsHistory bit NOT NULL
+ ,RawResource varbinary(max) NULL
+ ,IsRawResourceMetaSet bit NOT NULL
+ ,RequestMethod varchar(10) NULL
+ ,SearchParamHash varchar(64) NULL
+ ,TransactionId bigint NULL
+ ,HistoryTransactionId bigint NULL
+
+ PRIMARY KEY (ResourceTypeId, ResourceSurrogateId)
+ ,UNIQUE (ResourceTypeId, ResourceIdInt, Version)
+ )
+ DECLARE @ReferenceSearchParamsWithIds AS TABLE
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,SearchParamId smallint NOT NULL
+ ,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
+ ,ReferenceResourceTypeId smallint NOT NULL
+ ,ReferenceResourceIdInt bigint NOT NULL
+
+ UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt)
+ )
+
+ -- reference search params Start ---------------------------------------------------------------------------
+ SET @st = getUTCdate()
+ INSERT INTO @InputIds
+ SELECT DISTINCT ReferenceResourceTypeId, ReferenceResourceId
+ FROM dbo.ReferenceSearchParamTbl
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId > @SurrogateId AND ResourceSurrogateId <= @CurrentMaxSurrogateId
+ AND ReferenceResourceTypeId IS NOT NULL
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Mode=@LastProcessed,@Target='ReferenceSearchParamTbl.@InputIds',@Action='Insert',@Rows=@@rowcount,@Start=@st
+
+ SET @st = getUTCdate()
+ INSERT INTO @ExistingIdsReference
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT A.ResourceTypeId, ResourceIdInt, A.ResourceId
+ FROM @InputIds A
+ JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+
+ INSERT INTO @InsertIds
+ ( ResourceTypeId, IdIndex, ResourceId )
+ SELECT ResourceTypeId, row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1, ResourceId
+ FROM @InputIds A
+ WHERE NOT EXISTS (SELECT * FROM @ExistingIdsReference B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId)
+
+ SET @NewIdsCount = (SELECT count(*) FROM @InsertIds)
+ IF @NewIdsCount > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @NewIdsCount, @FirstIdInt OUT
+
+ INSERT INTO @InsertedIdsReference
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, IdIndex + @FirstIdInt, ResourceId
+ FROM @InsertIds
+ END
+
+ INSERT INTO @ReferenceSearchParamsWithIds
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT A.ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, isnull(C.ResourceIdInt,B.ResourceIdInt)
+ FROM (SELECT TOP (@DummyTop) *
+ FROM dbo.ReferenceSearchParamTbl
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId > @SurrogateId AND ResourceSurrogateId <= @CurrentMaxSurrogateId
+ AND ReferenceResourceTypeId IS NOT NULL
+ ) A
+ LEFT OUTER JOIN @InsertedIdsReference B ON B.ResourceTypeId = A.ReferenceResourceTypeId AND B.ResourceId = A.ReferenceResourceId
+ LEFT OUTER JOIN @ExistingIdsReference C ON C.ResourceTypeId = A.ReferenceResourceTypeId AND C.ResourceId = A.ReferenceResourceId
+ OPTION (OPTIMIZE FOR (@DummyTop = 1))
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Mode=@LastProcessed,@Target='@ReferenceSearchParamsWithIds',@Action='Insert',@Rows=@@rowcount,@Start=@st
+
+ BEGIN TRANSACTION
+
+ SET @st = getUTCdate()
+ INSERT INTO dbo.ResourceIdIntMap
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, ResourceIdInt, ResourceId
+ FROM @InsertedIdsReference
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Mode=@LastProcessed,@Target='Reference.ResourceIdIntMap',@Action='Insert',@Rows=@@rowcount,@Start=@st
+
+ SET @st = getUTCdate()
+ INSERT INTO dbo.ResourceReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt
+ FROM @ReferenceSearchParamsWithIds
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Mode=@LastProcessed,@Target='ResourceReferenceSearchParams',@Action='Insert',@Rows=@@rowcount,@Start=@st
+
+ SET @st = getUTCdate()
+ INSERT INTO dbo.StringReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId
+ FROM dbo.ReferenceSearchParamTbl
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId > @SurrogateId AND ResourceSurrogateId <= @CurrentMaxSurrogateId
+ AND ReferenceResourceTypeId IS NULL
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Mode=@LastProcessed,@Target='StringReferenceSearchParams',@Action='Insert',@Rows=@@rowcount,@Start=@st
+
+ SET @st = getUTCdate()
+ DELETE FROM A
+ FROM dbo.ReferenceSearchParamTbl A WITH (PAGLOCK)
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId > @SurrogateId AND ResourceSurrogateId <= @CurrentMaxSurrogateId
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Mode=@LastProcessed,@Target='ReferenceSearchParamTbl',@Action='Delete',@Rows=@@rowcount,@Start=@st
+
+ COMMIT TRANSACTION
+ -- reference search params End ---------------------------------------------------------------------------
+
+ -- resources Start ---------------------------------------------------------------------------
+ SET @st = getUTCdate()
+ DELETE FROM @InputIds
+ INSERT INTO @InputIds
+ SELECT @ResourceTypeId, ResourceId
+ FROM (SELECT ResourceId
+ FROM dbo.ResourceTbl WITH (INDEX = 1)
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId > @SurrogateId AND ResourceSurrogateId <= @CurrentMaxSurrogateId
+ ) A
+ GROUP BY ResourceId
+ OPTION (MAXDOP 1)
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Mode=@LastProcessed,@Target='ResourceTbl.@InputIds',@Action='Insert',@Rows=@@rowcount,@Start=@st
+
+ SET @st = getUTCdate()
+ INSERT INTO @ExistingIdsResource
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT A.ResourceTypeId, isnull(C.ResourceIdInt,B.ResourceIdInt), A.ResourceId
+ FROM @InputIds A
+ LEFT OUTER JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN @InsertedIdsReference C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceId = A.ResourceId
+ WHERE C.ResourceIdInt IS NOT NULL OR B.ResourceIdInt IS NOT NULL
+
+ DELETE FROM @InsertIds
+ INSERT INTO @InsertIds
+ ( ResourceTypeId, IdIndex, ResourceId )
+ SELECT ResourceTypeId, row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1, ResourceId
+ FROM @InputIds A
+ WHERE NOT EXISTS (SELECT * FROM @ExistingIdsResource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId)
+
+ SET @NewIdsCount = (SELECT count(*) FROM @InsertIds)
+ IF @NewIdsCount > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @NewIdsCount, @FirstIdInt OUT
+
+ INSERT INTO @InsertedIdsResource
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, IdIndex + @FirstIdInt, ResourceId
+ FROM @InsertIds
+ END
+
+ INSERT INTO @ResourcesWithIds
+ ( ResourceTypeId, ResourceId, ResourceIdInt, Version, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, RawResource, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId )
+ SELECT A.ResourceTypeId, A.ResourceId, isnull(C.ResourceIdInt,B.ResourceIdInt), Version, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, RawResource, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId
+ FROM dbo.ResourceTbl A
+ LEFT OUTER JOIN @InsertedIdsResource B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN @ExistingIdsResource C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceId = A.ResourceId
+ WHERE A.ResourceTypeId = @ResourceTypeId
+ AND A.ResourceSurrogateId > @SurrogateId AND ResourceSurrogateId <= @CurrentMaxSurrogateId
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Mode=@LastProcessed,@Target='@ResourcesWithIds',@Action='Insert',@Rows=@@rowcount,@Start=@st
+
+ BEGIN TRANSACTION
+
+ SET @st = getUTCdate()
+ DELETE FROM A
+ FROM dbo.ResourceTbl A WITH (PAGLOCK)
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId > @SurrogateId AND ResourceSurrogateId <= @CurrentMaxSurrogateId
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Mode=@LastProcessed,@Target='ResourceTbl',@Action='Delete',@Rows=@@rowcount,@Start=@st
+
+ SET @st = getUTCdate()
+ INSERT INTO dbo.ResourceIdIntMap
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, ResourceIdInt, ResourceId
+ FROM @InsertedIdsResource
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Mode=@LastProcessed,@Target='Resource.ResourceIdIntMap',@Action='Insert',@Rows=@@rowcount,@Start=@st
+
+ SET @st = getUTCdate()
+ INSERT INTO dbo.Resource
+ ( ResourceTypeId, ResourceIdInt, Version, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, RawResource, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId )
+ SELECT ResourceTypeId, ResourceIdInt, Version, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, RawResource, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId
+ FROM @ResourcesWithIds
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Mode=@LastProcessed,@Target='Resource',@Action='Insert',@Rows=@@rowcount,@Start=@st
+
+ UPDATE dbo.Parameters SET Char = @LastProcessed WHERE Id = @Id
+
+ COMMIT TRANSACTION
+ -- resources End ---------------------------------------------------------------------------
+END TRY
+BEGIN CATCH
+ IF @@trancount > 0 ROLLBACK TRANSACTION
+
+ EXECUTE dbo.LogEvent @Process=@Process,@Mode=@LastProcessed,@Status='Error',@Start=@st
+
+ IF error_number() IN (2601, 2627) AND error_message() LIKE '%''dbo.ResourceIdIntMap''%' -- pk violation
+ OR error_number() = 547 AND error_message() LIKE '%DELETE%' -- reference violation on DELETE
+ BEGIN
+ DELETE FROM @ResourcesWithIds
+ DELETE FROM @ReferenceSearchParamsWithIds
+ DELETE FROM @InputIds
+ DELETE FROM @InsertIds
+ DELETE FROM @InsertedIdsReference
+ DELETE FROM @ExistingIdsReference
+ DELETE FROM @InsertedIdsResource
+ DELETE FROM @ExistingIdsResource
+
+ GOTO RetryResourceIdIntMapLogic
+ END
+ ELSE
+ THROW
+END CATCH
+GO
+-- Move data
+-- ROLLBACK TRANSACTION
+set nocount on
+INSERT INTO dbo.Parameters (Id, Char) SELECT 'LakeSchemaUpgrade', 'LogEvent'
+
+DECLARE @Types TABLE (ResourceTypeId smallint PRIMARY KEY, Name varchar(100))
+DECLARE @MaxSurrogateId bigint = 0
+ ,@ResourceTypeId smallint
+
+IF NOT EXISTS (SELECT * FROM dbo.Parameters WHERE Id = 'LakeSchemaUpgrade.MaxSurrogateId') -- DELETE FROM dbo.Parameters WHERE Id = 'LakeSchemaUpgrade.MaxSurrogateId'
+BEGIN
+ DECLARE @MaxSurrogateIdTmp bigint
+
+ INSERT INTO @Types EXECUTE dbo.GetUsedResourceTypes
+ WHILE EXISTS (SELECT * FROM @Types)
+ BEGIN
+ SET @ResourceTypeId = (SELECT TOP 1 ResourceTypeId FROM @Types)
+ SET @MaxSurrogateIdTmp = (SELECT max(ResourceSurrogateId) FROM Resource WHERE ResourceTypeId = @ResourceTypeId)
+ IF @MaxSurrogateIdTmp > @MaxSurrogateId SET @MaxSurrogateId = @MaxSurrogateIdTmp
+ DELETE FROM @Types WHERE ResourceTypeId = @ResourceTypeId
+ END
+ INSERT INTO dbo.Parameters (Id, Bigint) SELECT 'LakeSchemaUpgrade.MaxSurrogateId', @MaxSurrogateId
+END
+
+SET @MaxSurrogateId = (SELECT Bigint FROM dbo.Parameters WHERE Id = 'LakeSchemaUpgrade.MaxSurrogateId')
+EXECUTE dbo.LogEvent @Process='LakeSchemaUpgrade',@Status='Run',@Target='@MaxSurrogateId',@Action='Select',@Text=@MaxSurrogateId
+
+DECLARE @Process varchar(100) = 'LakeSchemaUpgrade.MoveResources'
+ ,@Id varchar(100) = 'LakeSchemaUpgrade.MoveResources.LastProcessed.TypeId.SurrogateId' -- SELECT * FROM Parameters
+ ,@SurrogateId bigint
+ ,@RowsToProcess int
+ ,@ProcessedResources int
+ ,@ReportDate datetime = getUTCdate()
+ ,@Rows int
+ ,@CurrentMaxSurrogateId bigint
+ ,@LastProcessed varchar(100)
+ ,@st datetime
+
+INSERT INTO dbo.Parameters (Id, Char) SELECT @Process, 'LogEvent'
+
+BEGIN TRY
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Start'
+
+ INSERT INTO dbo.Parameters (Id, Char) SELECT @Id, '0.0'
+
+ SET @LastProcessed = (SELECT Char FROM dbo.Parameters WHERE Id = @Id)
+
+ INSERT INTO @Types EXECUTE dbo.GetUsedResourceTypes
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='@Types',@Action='Insert',@Rows=@@rowcount
+
+ SET @ResourceTypeId = substring(@LastProcessed, 1, charindex('.', @LastProcessed) - 1) -- (SELECT value FROM string_split(@LastProcessed, '.', 1) WHERE ordinal = 1)
+ SET @SurrogateId = substring(@LastProcessed, charindex('.', @LastProcessed) + 1, 255) -- (SELECT value FROM string_split(@LastProcessed, '.', 1) WHERE ordinal = 2)
+
+ DELETE FROM @Types WHERE ResourceTypeId < @ResourceTypeId
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='@Types',@Action='Delete',@Rows=@@rowcount
+
+ WHILE EXISTS (SELECT * FROM @Types) -- Processing in ASC order
+ BEGIN
+ SET @ResourceTypeId = (SELECT TOP 1 ResourceTypeId FROM @Types ORDER BY ResourceTypeId)
+
+ SET @ProcessedResources = 0
+ SET @CurrentMaxSurrogateId = 0
+ WHILE @CurrentMaxSurrogateId IS NOT NULL
+ BEGIN
+ SET @CurrentMaxSurrogateId = NULL
+ SELECT @CurrentMaxSurrogateId = max(ResourceSurrogateId), @RowsToProcess = count(*)
+ FROM (SELECT TOP 5000 ResourceSurrogateId
+ FROM dbo.ResourceTbl
+ WHERE ResourceTypeId = @ResourceTypeId AND ResourceSurrogateId > @SurrogateId AND ResourceSurrogateId <= @MaxSurrogateId
+ ORDER BY ResourceSurrogateId
+ ) A
+
+ IF @CurrentMaxSurrogateId IS NOT NULL
+ BEGIN
+ SET @LastProcessed = convert(varchar,@ResourceTypeId)+'.'+convert(varchar,@CurrentMaxSurrogateId)
+
+ EXECUTE tmp_MoveResources @ResourceTypeId = @ResourceTypeId, @SurrogateId = @SurrogateId, @CurrentMaxSurrogateId = @CurrentMaxSurrogateId, @LastProcessed = @LastProcessed
+
+ SET @SurrogateId = @CurrentMaxSurrogateId
+
+ SET @ProcessedResources += @RowsToProcess
+
+ IF datediff(second, @ReportDate, getUTCdate()) > 60
+ BEGIN
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Mode=@LastProcessed,@Target='Resource',@Action='Select',@Rows=@ProcessedResources
+ SET @ReportDate = getUTCdate()
+ SET @ProcessedResources = 0
+ END
+ END
+ ELSE
+ BEGIN
+ SET @LastProcessed = convert(varchar,@ResourceTypeId)+'.'+convert(varchar,@MaxSurrogateId)
+ UPDATE dbo.Parameters SET Char = @LastProcessed WHERE Id = @Id
+ END
+ END
+
+ IF @ProcessedResources > 0
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Mode=@LastProcessed,@Target='Resource',@Action='Select',@Rows=@ProcessedResources
+
+ DELETE FROM @Types WHERE ResourceTypeId = @ResourceTypeId
+
+ SET @SurrogateId = 0
+ END
+
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='End'
+
+ IF 0 < (SELECT sum(row_count) FROM sys.dm_db_partition_stats WHERE object_Id = object_id('ResourceTbl') AND index_id IN (0,1))
+ RAISERROR('ResourceTbl is not empty', 18, 127)
+
+ IF 0 < (SELECT sum(row_count) FROM sys.dm_db_partition_stats WHERE object_Id = object_id('ReferenceSearchParamTbl') AND index_id IN (0,1))
+ RAISERROR('ReferenceSearchParamTbl is not empty', 18, 127)
+
+ EXECUTE('
+ALTER VIEW dbo.ReferenceSearchParam
+AS
+SELECT A.ResourceTypeId
+ ,ResourceSurrogateId
+ ,SearchParamId
+ ,BaseUri
+ ,ReferenceResourceTypeId
+ ,ReferenceResourceId = B.ResourceId
+ ,ReferenceResourceIdInt
+ ,IsResourceRef
+ FROM dbo.ResourceReferenceSearchParams A
+ LEFT OUTER JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ReferenceResourceTypeId AND B.ResourceIdInt = A.ReferenceResourceIdInt
+UNION ALL
+SELECT ResourceTypeId
+ ,ResourceSurrogateId
+ ,SearchParamId
+ ,BaseUri
+ ,NULL
+ ,ReferenceResourceId
+ ,NULL
+ ,IsResourceRef
+ FROM dbo.StringReferenceSearchParams
+ ')
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='ReferenceSearchParam',@Action='Alter'
+
+ EXECUTE('
+ALTER VIEW dbo.Resource
+AS
+SELECT A.ResourceTypeId
+ ,A.ResourceSurrogateId
+ ,ResourceId
+ ,A.ResourceIdInt
+ ,Version
+ ,IsHistory
+ ,IsDeleted
+ ,RequestMethod
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,TransactionId
+ ,HistoryTransactionId
+ ,FileId
+ ,OffsetInFile
+ FROM dbo.CurrentResources A
+ LEFT OUTER JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ LEFT OUTER JOIN dbo.ResourceIdIntMap C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = A.ResourceIdInt
+UNION ALL
+SELECT A.ResourceTypeId
+ ,A.ResourceSurrogateId
+ ,ResourceId
+ ,A.ResourceIdInt
+ ,Version
+ ,IsHistory
+ ,IsDeleted
+ ,RequestMethod
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,TransactionId
+ ,HistoryTransactionId
+ ,FileId
+ ,OffsetInFile
+ FROM dbo.HistoryResources A
+ LEFT OUTER JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ LEFT OUTER JOIN dbo.ResourceIdIntMap C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = A.ResourceIdInt
+ ')
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='Resource',@Action='Alter'
+
+ EXECUTE('
+ALTER VIEW dbo.CurrentResource
+AS
+SELECT A.ResourceTypeId
+ ,A.ResourceSurrogateId
+ ,ResourceId
+ ,A.ResourceIdInt
+ ,Version
+ ,IsHistory
+ ,IsDeleted
+ ,RequestMethod
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,TransactionId
+ ,HistoryTransactionId
+ ,FileId
+ ,OffsetInFile
+ FROM dbo.CurrentResources A
+ LEFT OUTER JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ LEFT OUTER JOIN dbo.ResourceIdIntMap C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = A.ResourceIdInt
+ ')
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='CurrentResource',@Action='Alter'
+
+ EXECUTE('
+ALTER TRIGGER dbo.ResourceIns ON dbo.Resource INSTEAD OF INSERT
+AS
+BEGIN
+ INSERT INTO dbo.RawResources
+ ( ResourceTypeId, ResourceSurrogateId, RawResource )
+ SELECT ResourceTypeId, ResourceSurrogateId, RawResource
+ FROM Inserted
+ WHERE RawResource IS NOT NULL
+
+ INSERT INTO dbo.CurrentResources
+ ( ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile )
+ SELECT ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile
+ FROM Inserted
+ WHERE IsHistory = 0
+
+ INSERT INTO dbo.HistoryResources
+ ( ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile )
+ SELECT ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile
+ FROM Inserted
+ WHERE IsHistory = 1
+END
+ ')
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='ResourceIns',@Action='Alter'
+
+ EXECUTE('
+ALTER TRIGGER dbo.ResourceUpd ON dbo.Resource INSTEAD OF UPDATE
+AS
+BEGIN
+ IF UPDATE(IsDeleted) AND UPDATE(RawResource) AND UPDATE(SearchParamHash) AND UPDATE(HistoryTransactionId) AND NOT UPDATE(IsHistory) -- hard delete resource
+ BEGIN
+ UPDATE B
+ SET RawResource = A.RawResource
+ FROM Inserted A
+ JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ IF @@rowcount = 0
+ INSERT INTO dbo.RawResources
+ ( ResourceTypeId, ResourceSurrogateId, RawResource )
+ SELECT ResourceTypeId, ResourceSurrogateId, RawResource
+ FROM Inserted
+ WHERE RawResource IS NOT NULL
+
+ UPDATE B
+ SET IsDeleted = A.IsDeleted
+ ,SearchParamHash = A.SearchParamHash
+ ,HistoryTransactionId = A.HistoryTransactionId
+ FROM Inserted A
+ JOIN dbo.CurrentResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ RETURN
+ END
+
+ IF UPDATE(SearchParamHash) AND NOT UPDATE(IsHistory) -- reindex
+ BEGIN
+ UPDATE B
+ SET SearchParamHash = A.SearchParamHash
+ FROM Inserted A
+ JOIN dbo.CurrentResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ WHERE A.IsHistory = 0
+
+ RETURN
+ END
+
+ IF UPDATE(TransactionId) AND NOT UPDATE(IsHistory) -- cleanup trans
+ BEGIN
+ UPDATE B
+ SET TransactionId = A.TransactionId
+ FROM Inserted A
+ JOIN dbo.CurrentResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 0
+
+ UPDATE B
+ SET TransactionId = A.TransactionId
+ FROM Inserted A
+ JOIN dbo.HistoryResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 1
+
+ RETURN
+ END
+
+ IF UPDATE(RawResource) -- invisible records
+ BEGIN
+ UPDATE B
+ SET RawResource = A.RawResource
+ FROM Inserted A
+ JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ IF @@rowcount = 0
+ INSERT INTO dbo.RawResources
+ ( ResourceTypeId, ResourceSurrogateId, RawResource )
+ SELECT ResourceTypeId, ResourceSurrogateId, RawResource
+ FROM Inserted
+ WHERE RawResource IS NOT NULL
+ END
+
+ IF NOT UPDATE(IsHistory)
+ RAISERROR(''Generic updates are not supported via Resource view'',18,127)
+
+ DELETE FROM A
+ FROM dbo.CurrentResources A
+ WHERE EXISTS (SELECT * FROM Inserted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 1)
+
+ INSERT INTO dbo.HistoryResources
+ ( ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile )
+ SELECT ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile
+ FROM Inserted
+ WHERE IsHistory = 1
+END
+ ')
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='ResourceUpd',@Action='Alter'
+
+ EXECUTE('
+ALTER TRIGGER dbo.ResourceDel ON dbo.Resource INSTEAD OF DELETE
+AS
+BEGIN
+ DELETE FROM A
+ FROM dbo.CurrentResources A
+ WHERE EXISTS (SELECT * FROM Deleted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 0)
+
+ DELETE FROM A
+ FROM dbo.HistoryResources A
+ WHERE EXISTS (SELECT * FROM Deleted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 1)
+
+ DELETE FROM A
+ FROM dbo.RawResources A
+ WHERE EXISTS (SELECT * FROM Deleted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId)
+END
+ ')
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='ResourceDel',@Action='Alter'
+
+ EXECUTE('
+ALTER PROCEDURE dbo.GetResourceVersions @ResourceDateKeys dbo.ResourceDateKeyList READONLY
+AS
+-- This stored procedure allows to identifiy if version gap is available and checks dups on lastUpdated
+set nocount on
+DECLARE @st datetime = getUTCdate()
+ ,@SP varchar(100) = ''GetResourceVersions''
+ ,@Mode varchar(100) = ''Rows=''+convert(varchar,(SELECT count(*) FROM @ResourceDateKeys))
+ ,@DummyTop bigint = 9223372036854775807
+
+BEGIN TRY
+ SELECT A.ResourceTypeId
+ ,A.ResourceId
+ ,A.ResourceSurrogateId
+ -- set version to 0 if there is no gap available, or lastUpdated is already used. It would indicate potential conflict for the caller.
+ ,Version = CASE
+ -- ResourceSurrogateId is generated from lastUpdated only without extra bits at the end. Need to ckeck interval (0..79999) on resource id level.
+ WHEN D.Version IS NOT NULL THEN 0 -- input lastUpdated matches stored
+ WHEN isnull(U.Version, 1) - isnull(L.Version, 0) > ResourceIndex THEN isnull(U.Version, 1) - ResourceIndex -- gap is available
+ ELSE isnull(M.Version, 0) - ResourceIndex -- late arrival
+ END
+ ,MatchedVersion = isnull(D.Version,0)
+ ,MatchedRawResource = D.RawResource
+ ,MatchedFileId = D.FileId
+ ,MatchedOffsetInFile = D.OffsetInFile
+ -- ResourceIndex allows to deal with more than one late arrival per resource
+ FROM (SELECT TOP (@DummyTop) A.*, M.ResourceIdInt, ResourceIndex = convert(int,row_number() OVER (PARTITION BY A.ResourceTypeId, A.ResourceId ORDER BY ResourceSurrogateId DESC))
+ FROM @ResourceDateKeys A
+ LEFT OUTER JOIN dbo.ResourceIdIntMap M WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON M.ResourceTypeId = A.ResourceTypeId AND M.ResourceId = A.ResourceId
+ ) A
+ OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt AND B.Version > 0 AND B.ResourceSurrogateId < A.ResourceSurrogateId ORDER BY B.ResourceSurrogateId DESC) L -- lower
+ OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt AND B.Version > 0 AND B.ResourceSurrogateId > A.ResourceSurrogateId ORDER BY B.ResourceSurrogateId) U -- upper
+ OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt AND B.Version < 0 ORDER BY B.Version) M -- minus
+ OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt AND B.ResourceSurrogateId BETWEEN A.ResourceSurrogateId AND A.ResourceSurrogateId + 79999) D -- date
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Start=@st,@Rows=@@rowcount
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error'',@Start=@st;
+ THROW
+END CATCH
+ ')
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='GetResourceVersions',@Action='Alter'
+
+ EXECUTE('
+ALTER PROCEDURE dbo.GetResources @ResourceKeys dbo.ResourceKeyList READONLY
+AS
+set nocount on
+DECLARE @st datetime = getUTCdate()
+ ,@SP varchar(100) = ''GetResources''
+ ,@InputRows int
+ ,@NotNullVersionExists bit
+ ,@NullVersionExists bit
+ ,@MinRT smallint
+ ,@MaxRT smallint
+
+SELECT @MinRT = min(ResourceTypeId), @MaxRT = max(ResourceTypeId), @InputRows = count(*), @NotNullVersionExists = max(CASE WHEN Version IS NOT NULL THEN 1 ELSE 0 END), @NullVersionExists = max(CASE WHEN Version IS NULL THEN 1 ELSE 0 END) FROM @ResourceKeys
+
+DECLARE @Mode varchar(100) = ''RT=[''+convert(varchar,@MinRT)+'',''+convert(varchar,@MaxRT)+''] Cnt=''+convert(varchar,@InputRows)+'' NNVE=''+convert(varchar,@NotNullVersionExists)+'' NVE=''+convert(varchar,@NullVersionExists)
+
+BEGIN TRY
+ IF @NotNullVersionExists = 1
+ IF @NullVersionExists = 0
+ SELECT B.ResourceTypeId
+ ,B.ResourceId
+ ,ResourceSurrogateId
+ ,C.Version
+ ,IsDeleted
+ ,IsHistory
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,FileId
+ ,OffsetInFile
+ FROM (SELECT * FROM @ResourceKeys) A
+ INNER LOOP JOIN dbo.ResourceIdIntMap B WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN dbo.Resource C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = B.ResourceIdInt AND C.Version = A.Version
+ OPTION (MAXDOP 1)
+ ELSE
+ SELECT *
+ FROM (SELECT B.ResourceTypeId
+ ,B.ResourceId
+ ,ResourceSurrogateId
+ ,C.Version
+ ,IsDeleted
+ ,IsHistory
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,FileId
+ ,OffsetInFile
+ FROM (SELECT * FROM @ResourceKeys WHERE Version IS NOT NULL) A
+ INNER LOOP JOIN dbo.ResourceIdIntMap B WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN dbo.Resource C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = B.ResourceIdInt AND C.Version = A.Version
+ UNION ALL
+ SELECT B.ResourceTypeId
+ ,B.ResourceId
+ ,C.ResourceSurrogateId
+ ,C.Version
+ ,IsDeleted
+ ,IsHistory
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,FileId
+ ,OffsetInFile
+ FROM (SELECT * FROM @ResourceKeys WHERE Version IS NULL) A
+ INNER LOOP JOIN dbo.ResourceIdIntMap B WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN dbo.CurrentResources C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = B.ResourceIdInt AND C.IsHistory = 0
+ LEFT OUTER JOIN dbo.RawResources D ON D.ResourceTypeId = A.ResourceTypeId AND D.ResourceSurrogateId = C.ResourceSurrogateId
+ ) A
+ OPTION (MAXDOP 1)
+ ELSE
+ SELECT B.ResourceTypeId
+ ,B.ResourceId
+ ,C.ResourceSurrogateId
+ ,C.Version
+ ,IsDeleted
+ ,IsHistory
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,FileId
+ ,OffsetInFile
+ FROM (SELECT * FROM @ResourceKeys) A
+ INNER LOOP JOIN dbo.ResourceIdIntMap B WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN dbo.CurrentResources C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = B.ResourceIdInt
+ LEFT OUTER JOIN dbo.RawResources D ON D.ResourceTypeId = A.ResourceTypeId AND D.ResourceSurrogateId = C.ResourceSurrogateId
+ OPTION (MAXDOP 1)
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Start=@st,@Rows=@@rowcount
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error'',@Start=@st;
+ THROW
+END CATCH
+ ')
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='GetResources',@Action='Alter'
+
+ EXECUTE('
+ALTER PROCEDURE dbo.UpdateResourceSearchParams
+ @FailedResources int = 0 OUT
+ ,@Resources dbo.ResourceList READONLY -- TODO: Remove after deployment
+ ,@ResourcesLake dbo.ResourceListLake READONLY
+ ,@ResourceWriteClaims dbo.ResourceWriteClaimList READONLY
+ ,@ReferenceSearchParams dbo.ReferenceSearchParamList READONLY
+ ,@TokenSearchParams dbo.TokenSearchParamList READONLY
+ ,@TokenTexts dbo.TokenTextList READONLY
+ ,@StringSearchParams dbo.StringSearchParamList READONLY
+ ,@UriSearchParams dbo.UriSearchParamList READONLY
+ ,@NumberSearchParams dbo.NumberSearchParamList READONLY
+ ,@QuantitySearchParams dbo.QuantitySearchParamList READONLY
+ ,@DateTimeSearchParams dbo.DateTimeSearchParamList READONLY
+ ,@ReferenceTokenCompositeSearchParams dbo.ReferenceTokenCompositeSearchParamList READONLY
+ ,@TokenTokenCompositeSearchParams dbo.TokenTokenCompositeSearchParamList READONLY
+ ,@TokenDateTimeCompositeSearchParams dbo.TokenDateTimeCompositeSearchParamList READONLY
+ ,@TokenQuantityCompositeSearchParams dbo.TokenQuantityCompositeSearchParamList READONLY
+ ,@TokenStringCompositeSearchParams dbo.TokenStringCompositeSearchParamList READONLY
+ ,@TokenNumberNumberCompositeSearchParams dbo.TokenNumberNumberCompositeSearchParamList READONLY
+AS
+set nocount on
+DECLARE @st datetime = getUTCdate()
+ ,@SP varchar(100) = object_name(@@procid)
+ ,@Mode varchar(200) = isnull((SELECT ''RT=[''+convert(varchar,min(ResourceTypeId))+'',''+convert(varchar,max(ResourceTypeId))+''] Sur=[''+convert(varchar,min(ResourceSurrogateId))+'',''+convert(varchar,max(ResourceSurrogateId))+''] V=''+convert(varchar,max(Version))+'' Rows=''+convert(varchar,count(*)) FROM (SELECT ResourceTypeId, ResourceSurrogateId, Version FROM @ResourcesLake UNION ALL SELECT ResourceTypeId, ResourceSurrogateId, Version FROM @Resources) A),''Input=Empty'')
+ ,@ResourceRows int
+ ,@InsertRows int
+ ,@DeletedIdMap int
+ ,@FirstIdInt bigint
+ ,@CurrentRows int
+
+RetryResourceIdIntMapLogic:
+BEGIN TRY
+ DECLARE @Ids TABLE (ResourceTypeId smallint NOT NULL, ResourceSurrogateId bigint NOT NULL)
+ DECLARE @CurrentRefIdsRaw TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL)
+ DECLARE @CurrentRefIds TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL PRIMARY KEY (ResourceTypeId, ResourceIdInt))
+ DECLARE @InputRefIds AS TABLE (ResourceTypeId smallint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ExistingRefIds AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertRefIds AS TABLE (ResourceTypeId smallint NOT NULL, IdIndex int NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertedRefIds AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ReferenceSearchParamsWithIds AS TABLE
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,SearchParamId smallint NOT NULL
+ ,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
+ ,ReferenceResourceTypeId smallint NULL
+ ,ReferenceResourceIdInt bigint NOT NULL
+ ,ReferenceResourceVersion int NULL
+
+ UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt)
+ )
+
+ -- Prepare insert into ResourceIdIntMap outside of transaction to minimize blocking
+ INSERT INTO @InputRefIds SELECT DISTINCT ReferenceResourceTypeId, ReferenceResourceId FROM @ReferenceSearchParams WHERE ReferenceResourceTypeId IS NOT NULL
+
+ INSERT INTO @ExistingRefIds
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT A.ResourceTypeId, ResourceIdInt, A.ResourceId
+ FROM @InputRefIds A
+ JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+
+ INSERT INTO @InsertRefIds
+ ( ResourceTypeId, IdIndex, ResourceId )
+ SELECT ResourceTypeId, row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1, ResourceId
+ FROM @InputRefIds A
+ WHERE NOT EXISTS (SELECT * FROM @ExistingRefIds B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId)
+
+ SET @InsertRows = (SELECT count(*) FROM @InsertRefIds)
+ IF @InsertRows > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @InsertRows, @FirstIdInt OUT
+
+ INSERT INTO @InsertedRefIds
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, IdIndex + @FirstIdInt, ResourceId
+ FROM @InsertRefIds
+ END
+
+ INSERT INTO @ReferenceSearchParamsWithIds
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt, ReferenceResourceVersion )
+ SELECT A.ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, isnull(C.ResourceIdInt,B.ResourceIdInt), ReferenceResourceVersion
+ FROM @ReferenceSearchParams A
+ LEFT OUTER JOIN @InsertedRefIds B ON B.ResourceTypeId = A.ReferenceResourceTypeId AND B.ResourceId = A.ReferenceResourceId
+ LEFT OUTER JOIN @ExistingRefIds C ON C.ResourceTypeId = A.ReferenceResourceTypeId AND C.ResourceId = A.ReferenceResourceId
+
+ BEGIN TRANSACTION
+
+ -- Update the search parameter hash value in the main resource table
+ IF EXISTS (SELECT * FROM @ResourcesLake)
+ UPDATE B
+ SET SearchParamHash = (SELECT SearchParamHash FROM @ResourcesLake A WHERE A.ResourceTypeId = B.ResourceTypeId AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ OUTPUT deleted.ResourceTypeId, deleted.ResourceSurrogateId INTO @Ids
+ FROM dbo.Resource B
+ WHERE EXISTS (SELECT * FROM @ResourcesLake A WHERE A.ResourceTypeId = B.ResourceTypeId AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ AND B.IsHistory = 0
+ ELSE
+ UPDATE B
+ SET SearchParamHash = (SELECT SearchParamHash FROM @Resources A WHERE A.ResourceTypeId = B.ResourceTypeId AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ OUTPUT deleted.ResourceTypeId, deleted.ResourceSurrogateId INTO @Ids
+ FROM dbo.Resource B
+ WHERE EXISTS (SELECT * FROM @Resources A WHERE A.ResourceTypeId = B.ResourceTypeId AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ AND B.IsHistory = 0
+ SET @ResourceRows = @@rowcount
+
+ -- First, delete all the search params of the resources to reindex.
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ResourceWriteClaim B ON B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B
+ OUTPUT deleted.ReferenceResourceTypeId, deleted.ReferenceResourceIdInt INTO @CurrentRefIdsRaw
+ FROM @Ids A INNER LOOP JOIN dbo.ResourceReferenceSearchParams B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.StringReferenceSearchParams B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenText B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.StringSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.UriSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.NumberSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.QuantitySearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.DateTimeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ReferenceTokenCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenTokenCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenDateTimeCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenQuantityCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenStringCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenNumberNumberCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ -- Next, insert all the new search params.
+ INSERT INTO dbo.ResourceWriteClaim
+ ( ResourceSurrogateId, ClaimTypeId, ClaimValue )
+ SELECT ResourceSurrogateId, ClaimTypeId, ClaimValue
+ FROM @ResourceWriteClaims
+
+ -- start delete logic from ResourceIdIntMap
+ INSERT INTO @CurrentRefIds SELECT DISTINCT ResourceTypeId, ResourceIdInt FROM @CurrentRefIdsRaw
+ SET @CurrentRows = @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove not reused
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM @ReferenceSearchParamsWithIds B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove referenced by resources
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove referenced by reference search params
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- finally delete from id map
+ DELETE FROM B FROM @CurrentRefIds A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap = @@rowcount
+ END
+ END
+ END
+ END
+
+ INSERT INTO dbo.ResourceIdIntMap
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, ResourceIdInt, ResourceId
+ FROM @InsertedRefIds
+
+ INSERT INTO dbo.ResourceReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt
+ FROM @ReferenceSearchParamsWithIds
+
+ INSERT INTO dbo.StringReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId
+ FROM @ReferenceSearchParams
+ WHERE ReferenceResourceTypeId IS NULL
+
+ INSERT INTO dbo.TokenSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow
+ FROM @TokenSearchParams
+
+ INSERT INTO dbo.TokenText
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Text )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Text
+ FROM @TokenTexts
+
+ INSERT INTO dbo.StringSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax
+ FROM @StringSearchParams
+
+ INSERT INTO dbo.UriSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri
+ FROM @UriSearchParams
+
+ INSERT INTO dbo.NumberSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue
+ FROM @NumberSearchParams
+
+ INSERT INTO dbo.QuantitySearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue
+ FROM @QuantitySearchParams
+
+ INSERT INTO dbo.DateTimeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax
+ FROM @DateTimeSearchParams
+
+ INSERT INTO dbo.ReferenceTokenCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2
+ FROM @ReferenceTokenCompositeSearchParams
+
+ INSERT INTO dbo.TokenTokenCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2
+ FROM @TokenTokenCompositeSearchParams
+
+ INSERT INTO dbo.TokenDateTimeCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2
+ FROM @TokenDateTimeCompositeSearchParams
+
+ INSERT INTO dbo.TokenQuantityCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2
+ FROM @TokenQuantityCompositeSearchParams
+
+ INSERT INTO dbo.TokenStringCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2
+ FROM @TokenStringCompositeSearchParams
+
+ INSERT INTO dbo.TokenNumberNumberCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange
+ FROM @TokenNumberNumberCompositeSearchParams
+
+ COMMIT TRANSACTION
+
+ SET @FailedResources = (SELECT count(*) FROM @Resources) + (SELECT count(*) FROM @ResourcesLake) - @ResourceRows
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Start=@st,@Rows=@ResourceRows,@Text=@DeletedIdMap
+END TRY
+BEGIN CATCH
+ IF @@trancount > 0 ROLLBACK TRANSACTION
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error'',@Start=@st
+
+ IF error_number() IN (2601, 2627) AND error_message() LIKE ''%''''dbo.ResourceIdIntMap''''%'' -- pk violation
+ OR error_number() = 547 AND error_message() LIKE ''%DELETE%'' -- reference violation on DELETE
+ BEGIN
+ DELETE FROM @Ids
+ DELETE FROM @InputRefIds
+ DELETE FROM @CurrentRefIdsRaw
+ DELETE FROM @CurrentRefIds
+ DELETE FROM @ExistingRefIds
+ DELETE FROM @InsertRefIds
+ DELETE FROM @InsertedRefIds
+ DELETE FROM @ReferenceSearchParamsWithIds
+
+ GOTO RetryResourceIdIntMapLogic
+ END
+ ELSE
+ THROW
+END CATCH
+ ')
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='UpdateResourceSearchParams',@Action='Alter'
+
+ EXECUTE('
+ALTER PROCEDURE dbo.HardDeleteResource
+ @ResourceTypeId smallint
+ ,@ResourceId varchar(64)
+ ,@KeepCurrentVersion bit
+ ,@IsResourceChangeCaptureEnabled bit = 0 -- TODO: Remove input parameter after deployment
+ ,@MakeResourceInvisible bit = 0
+AS
+set nocount on
+DECLARE @SP varchar(100) = object_name(@@procid)
+ ,@Mode varchar(200) = ''RT=''+convert(varchar,@ResourceTypeId)+'' R=''+@ResourceId+'' V=''+convert(varchar,@KeepCurrentVersion)
+ ,@st datetime = getUTCdate()
+ ,@TransactionId bigint
+ ,@DeletedIdMap int = 0
+ ,@Rows int
+
+IF @IsResourceChangeCaptureEnabled = 1
+ SET @MakeResourceInvisible = 1
+
+SET @Mode += '' I=''+convert(varchar,@MakeResourceInvisible)
+
+IF @MakeResourceInvisible = 1
+BEGIN
+ EXECUTE dbo.MergeResourcesBeginTransaction @Count = 1, @TransactionId = @TransactionId OUT
+ SET @Mode += '' T=''+convert(varchar,@TransactionId)
+END
+
+DECLARE @Ids TABLE (ResourceSurrogateId bigint NOT NULL, ResourceIdInt bigint NOT NULL)
+DECLARE @IdsDistinct TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL PRIMARY KEY (ResourceTypeId, ResourceIdInt))
+DECLARE @RefIdsRaw TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL)
+
+RetryResourceIdIntMapLogic:
+BEGIN TRY
+ BEGIN TRANSACTION
+
+ IF @MakeResourceInvisible = 1
+ UPDATE dbo.Resource
+ SET IsDeleted = 1
+ ,RawResource = 0xF -- invisible value
+ ,SearchParamHash = NULL
+ ,HistoryTransactionId = @TransactionId
+ OUTPUT deleted.ResourceSurrogateId, deleted.ResourceIdInt INTO @Ids
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceId = @ResourceId
+ AND (@KeepCurrentVersion = 0 OR IsHistory = 1)
+ AND (RawResource IS NULL -- stored in ADLS
+ OR RawResource <> 0xF -- stored in the database and not already invisible
+ )
+ ELSE
+ BEGIN
+ DELETE dbo.Resource
+ OUTPUT deleted.ResourceSurrogateId, deleted.ResourceIdInt INTO @Ids
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceId = @ResourceId
+ AND (@KeepCurrentVersion = 0 OR IsHistory = 1)
+ AND RawResource <> 0xF
+
+ INSERT INTO @IdsDistinct SELECT DISTINCT @ResourceTypeId, ResourceIdInt FROM @Ids
+ SET @Rows = @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM A FROM @IdsDistinct A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM A FROM @IdsDistinct A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM B FROM @IdsDistinct A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap = @@rowcount
+ END
+ END
+ END
+ END
+
+ IF @KeepCurrentVersion = 0
+ BEGIN
+ -- PAGLOCK allows deallocation of empty page without waiting for ghost cleanup
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ResourceWriteClaim B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B
+ OUTPUT deleted.ReferenceResourceTypeId, deleted.ReferenceResourceIdInt INTO @RefIdsRaw
+ FROM @Ids A INNER LOOP JOIN dbo.ResourceReferenceSearchParams B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM @IdsDistinct -- is used above
+ INSERT INTO @IdsDistinct SELECT DISTINCT ResourceTypeId, ResourceIdInt FROM @RefIdsRaw
+ SET @Rows = @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM A FROM @IdsDistinct A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM A FROM @IdsDistinct A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM B FROM @IdsDistinct A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap += @@rowcount
+ END
+ END
+ END
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.StringReferenceSearchParams B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenText B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.StringSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.UriSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.NumberSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.QuantitySearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.DateTimeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ReferenceTokenCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenTokenCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenDateTimeCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenQuantityCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenStringCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenNumberNumberCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ END
+
+ COMMIT TRANSACTION
+
+ IF @MakeResourceInvisible = 1
+ EXECUTE dbo.MergeResourcesCommitTransaction @TransactionId
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Start=@st,@Text=@DeletedIdMap
+END TRY
+BEGIN CATCH
+ IF @@trancount > 0 ROLLBACK TRANSACTION
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error'',@Start=@st
+
+ IF error_number() = 547 AND error_message() LIKE ''%DELETE%''-- reference violation on DELETE
+ BEGIN
+ DELETE FROM @Ids
+ DELETE FROM @RefIdsRaw
+ DELETE FROM @IdsDistinct
+ GOTO RetryResourceIdIntMapLogic
+ END
+ ELSE
+ THROW
+END CATCH
+ ')
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='HardDeleteResource',@Action='Alter'
+
+ EXECUTE('
+ALTER PROCEDURE dbo.MergeResources
+-- This stored procedure can be used for:
+-- 1. Ordinary put with single version per resource in input
+-- 2. Put with history preservation (multiple input versions per resource)
+-- 3. Copy from one gen2 store to another with ResourceSurrogateId preserved.
+ @AffectedRows int = 0 OUT
+ ,@RaiseExceptionOnConflict bit = 1
+ ,@IsResourceChangeCaptureEnabled bit = 0
+ ,@TransactionId bigint = NULL
+ ,@SingleTransaction bit = 1
+ ,@Resources dbo.ResourceList READONLY -- before lake code. TODO: Remove after deployment
+ ,@ResourcesLake dbo.ResourceListLake READONLY -- Lake code
+ ,@ResourceWriteClaims dbo.ResourceWriteClaimList READONLY
+ ,@ReferenceSearchParams dbo.ReferenceSearchParamList READONLY
+ ,@TokenSearchParams dbo.TokenSearchParamList READONLY
+ ,@TokenTexts dbo.TokenTextList READONLY
+ ,@StringSearchParams dbo.StringSearchParamList READONLY
+ ,@UriSearchParams dbo.UriSearchParamList READONLY
+ ,@NumberSearchParams dbo.NumberSearchParamList READONLY
+ ,@QuantitySearchParams dbo.QuantitySearchParamList READONLY
+ ,@DateTimeSearchParms dbo.DateTimeSearchParamList READONLY
+ ,@ReferenceTokenCompositeSearchParams dbo.ReferenceTokenCompositeSearchParamList READONLY
+ ,@TokenTokenCompositeSearchParams dbo.TokenTokenCompositeSearchParamList READONLY
+ ,@TokenDateTimeCompositeSearchParams dbo.TokenDateTimeCompositeSearchParamList READONLY
+ ,@TokenQuantityCompositeSearchParams dbo.TokenQuantityCompositeSearchParamList READONLY
+ ,@TokenStringCompositeSearchParams dbo.TokenStringCompositeSearchParamList READONLY
+ ,@TokenNumberNumberCompositeSearchParams dbo.TokenNumberNumberCompositeSearchParamList READONLY
+AS
+set nocount on
+DECLARE @st datetime = getUTCdate()
+ ,@SP varchar(100) = object_name(@@procid)
+ ,@DummyTop bigint = 9223372036854775807
+ ,@InitialTranCount int = @@trancount
+ ,@IsRetry bit = 0
+ ,@RT smallint
+ ,@NewIdsCount int
+ ,@FirstIdInt bigint
+ ,@CurrentRows int
+ ,@DeletedIdMap int
+
+DECLARE @Mode varchar(200) = isnull((SELECT ''RT=[''+convert(varchar,min(ResourceTypeId))+'',''+convert(varchar,max(ResourceTypeId))+''] Sur=[''+convert(varchar,min(ResourceSurrogateId))+'',''+convert(varchar,max(ResourceSurrogateId))+''] V=''+convert(varchar,max(Version))+'' Rows=''+convert(varchar,count(*)) FROM (SELECT ResourceTypeId, ResourceSurrogateId, Version FROM @Resources UNION ALL SELECT ResourceTypeId, ResourceSurrogateId, Version FROM @ResourcesLake) A),''Input=Empty'')
+SET @Mode += '' E=''+convert(varchar,@RaiseExceptionOnConflict)+'' CC=''+convert(varchar,@IsResourceChangeCaptureEnabled)+'' IT=''+convert(varchar,@InitialTranCount)+'' T=''+isnull(convert(varchar,@TransactionId),''NULL'')
+
+SET @AffectedRows = 0
+
+RetryResourceIdIntMapLogic:
+BEGIN TRY
+ DECLARE @InputIds AS TABLE (ResourceTypeId smallint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @CurrentRefIdsRaw TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL)
+ DECLARE @CurrentRefIds TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL PRIMARY KEY (ResourceTypeId, ResourceIdInt))
+ DECLARE @ExistingIdsReference AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ExistingIdsResource AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertIds AS TABLE (ResourceTypeId smallint NOT NULL, IdIndex int NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertedIdsReference AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertedIdsResource AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ResourcesWithIds AS TABLE
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL
+ ,ResourceIdInt bigint NOT NULL
+ ,Version int NOT NULL
+ ,HasVersionToCompare bit NOT NULL -- in case of multiple versions per resource indicates that row contains (existing version + 1) value
+ ,IsDeleted bit NOT NULL
+ ,IsHistory bit NOT NULL
+ ,KeepHistory bit NOT NULL
+ ,RawResource varbinary(max) NULL
+ ,IsRawResourceMetaSet bit NOT NULL
+ ,RequestMethod varchar(10) NULL
+ ,SearchParamHash varchar(64) NULL
+ ,FileId bigint NULL
+ ,OffsetInFile int NULL
+
+ PRIMARY KEY (ResourceTypeId, ResourceSurrogateId)
+ ,UNIQUE (ResourceTypeId, ResourceIdInt, Version)
+ )
+ DECLARE @ReferenceSearchParamsWithIds AS TABLE
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,SearchParamId smallint NOT NULL
+ ,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
+ ,ReferenceResourceTypeId smallint NOT NULL
+ ,ReferenceResourceIdInt bigint NOT NULL
+
+ UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt)
+ )
+
+ -- Prepare id map for reference search params Start ---------------------------------------------------------------------------
+ INSERT INTO @InputIds SELECT DISTINCT ReferenceResourceTypeId, ReferenceResourceId FROM @ReferenceSearchParams WHERE ReferenceResourceTypeId IS NOT NULL
+
+ INSERT INTO @ExistingIdsReference
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT A.ResourceTypeId, ResourceIdInt, A.ResourceId
+ FROM @InputIds A
+ JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+
+ INSERT INTO @InsertIds
+ ( ResourceTypeId, IdIndex, ResourceId )
+ SELECT ResourceTypeId, row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1, ResourceId
+ FROM @InputIds A
+ WHERE NOT EXISTS (SELECT * FROM @ExistingIdsReference B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId)
+
+ SET @NewIdsCount = (SELECT count(*) FROM @InsertIds)
+ IF @NewIdsCount > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @NewIdsCount, @FirstIdInt OUT
+
+ INSERT INTO @InsertedIdsReference
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, IdIndex + @FirstIdInt, ResourceId
+ FROM @InsertIds
+ END
+
+ INSERT INTO @ReferenceSearchParamsWithIds
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT A.ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, isnull(C.ResourceIdInt,B.ResourceIdInt)
+ FROM @ReferenceSearchParams A
+ LEFT OUTER JOIN @InsertedIdsReference B ON B.ResourceTypeId = A.ReferenceResourceTypeId AND B.ResourceId = A.ReferenceResourceId
+ LEFT OUTER JOIN @ExistingIdsReference C ON C.ResourceTypeId = A.ReferenceResourceTypeId AND C.ResourceId = A.ReferenceResourceId
+ WHERE ReferenceResourceTypeId IS NOT NULL
+ -- Prepare id map for reference search params End ---------------------------------------------------------------------------
+
+ -- Prepare id map for resources Start ---------------------------------------------------------------------------
+ DELETE FROM @InputIds
+ IF EXISTS (SELECT * FROM @ResourcesLake)
+ INSERT INTO @InputIds SELECT ResourceTypeId, ResourceId FROM @ResourcesLake GROUP BY ResourceTypeId, ResourceId
+ ELSE
+ INSERT INTO @InputIds SELECT ResourceTypeId, ResourceId FROM @Resources GROUP BY ResourceTypeId, ResourceId
+
+ INSERT INTO @ExistingIdsResource
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT A.ResourceTypeId, isnull(C.ResourceIdInt,B.ResourceIdInt), A.ResourceId
+ FROM @InputIds A
+ LEFT OUTER JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN @InsertedIdsReference C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceId = A.ResourceId
+ WHERE C.ResourceIdInt IS NOT NULL OR B.ResourceIdInt IS NOT NULL
+
+ DELETE FROM @InsertIds
+ INSERT INTO @InsertIds
+ ( ResourceTypeId, IdIndex, ResourceId )
+ SELECT ResourceTypeId, row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1, ResourceId
+ FROM @InputIds A
+ WHERE NOT EXISTS (SELECT * FROM @ExistingIdsResource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId)
+
+ SET @NewIdsCount = (SELECT count(*) FROM @InsertIds)
+ IF @NewIdsCount > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @NewIdsCount, @FirstIdInt OUT
+
+ INSERT INTO @InsertedIdsResource
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, IdIndex + @FirstIdInt, ResourceId
+ FROM @InsertIds
+ END
+
+ IF EXISTS (SELECT * FROM @ResourcesLake)
+ INSERT INTO @ResourcesWithIds
+ ( ResourceTypeId, ResourceId, ResourceIdInt, Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, FileId, OffsetInFile )
+ SELECT A.ResourceTypeId, A.ResourceId, isnull(C.ResourceIdInt,B.ResourceIdInt), Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, FileId, OffsetInFile
+ FROM @ResourcesLake A
+ LEFT OUTER JOIN @InsertedIdsResource B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN @ExistingIdsResource C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceId = A.ResourceId
+ ELSE
+ INSERT INTO @ResourcesWithIds
+ ( ResourceTypeId, ResourceId, ResourceIdInt, Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, FileId, OffsetInFile )
+ SELECT A.ResourceTypeId, A.ResourceId, isnull(C.ResourceIdInt,B.ResourceIdInt), Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, NULL, NULL
+ FROM @Resources A
+ LEFT OUTER JOIN @InsertedIdsResource B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN @ExistingIdsResource C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceId = A.ResourceId
+ -- Prepare id map for resources End ---------------------------------------------------------------------------
+
+ DECLARE @Existing AS TABLE (ResourceTypeId smallint NOT NULL, SurrogateId bigint NOT NULL PRIMARY KEY (ResourceTypeId, SurrogateId))
+
+ DECLARE @ResourceInfos AS TABLE
+ (
+ ResourceTypeId smallint NOT NULL
+ ,SurrogateId bigint NOT NULL
+ ,Version int NOT NULL
+ ,KeepHistory bit NOT NULL
+ ,PreviousVersion int NULL
+ ,PreviousSurrogateId bigint NULL
+
+ PRIMARY KEY (ResourceTypeId, SurrogateId)
+ )
+
+ DECLARE @PreviousSurrogateIds AS TABLE (TypeId smallint NOT NULL, SurrogateId bigint NOT NULL PRIMARY KEY (TypeId, SurrogateId), KeepHistory bit)
+
+ IF @SingleTransaction = 0 AND isnull((SELECT Number FROM dbo.Parameters WHERE Id = ''MergeResources.NoTransaction.IsEnabled''),0) = 0
+ SET @SingleTransaction = 1
+
+ SET @Mode += '' ST=''+convert(varchar,@SingleTransaction)
+
+ -- perform retry check in transaction to hold locks
+ IF @InitialTranCount = 0
+ BEGIN
+ IF EXISTS (SELECT * -- This extra statement avoids putting range locks when we don''t need them
+ FROM @ResourcesWithIds A JOIN dbo.Resource B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ WHERE B.IsHistory = 0
+ )
+ BEGIN
+ BEGIN TRANSACTION
+
+ INSERT INTO @Existing
+ ( ResourceTypeId, SurrogateId )
+ SELECT B.ResourceTypeId, B.ResourceSurrogateId
+ FROM (SELECT TOP (@DummyTop) * FROM @ResourcesWithIds) A
+ JOIN dbo.Resource B WITH (ROWLOCK, HOLDLOCK) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ WHERE B.IsHistory = 0
+ AND B.ResourceId = A.ResourceId
+ AND B.Version = A.Version
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+
+ IF @@rowcount = (SELECT count(*) FROM @ResourcesWithIds) SET @IsRetry = 1
+
+ IF @IsRetry = 0 COMMIT TRANSACTION -- commit check transaction
+ END
+ END
+
+ SET @Mode += '' R=''+convert(varchar,@IsRetry)
+
+ IF @SingleTransaction = 1 AND @@trancount = 0 BEGIN TRANSACTION
+
+ IF @IsRetry = 0
+ BEGIN
+ INSERT INTO @ResourceInfos
+ ( ResourceTypeId, SurrogateId, Version, KeepHistory, PreviousVersion, PreviousSurrogateId )
+ SELECT A.ResourceTypeId, A.ResourceSurrogateId, A.Version, A.KeepHistory, B.Version, B.ResourceSurrogateId
+ FROM (SELECT TOP (@DummyTop) * FROM @ResourcesWithIds WHERE HasVersionToCompare = 1) A
+ LEFT OUTER JOIN dbo.CurrentResources B -- WITH (UPDLOCK, HOLDLOCK) These locking hints cause deadlocks and are not needed. Racing might lead to tries to insert dups in unique index (with version key), but it will fail anyway, and in no case this will cause incorrect data saved.
+ ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+
+ IF @RaiseExceptionOnConflict = 1 AND EXISTS (SELECT * FROM @ResourceInfos WHERE PreviousVersion IS NOT NULL AND Version <= PreviousVersion)
+ THROW 50409, ''Resource has been recently updated or added, please compare the resource content in code for any duplicate updates'', 1
+
+ INSERT INTO @PreviousSurrogateIds
+ SELECT ResourceTypeId, PreviousSurrogateId, KeepHistory
+ FROM @ResourceInfos
+ WHERE PreviousSurrogateId IS NOT NULL
+
+ IF @@rowcount > 0
+ BEGIN
+ UPDATE dbo.Resource
+ SET IsHistory = 1
+ WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId AND KeepHistory = 1)
+ SET @AffectedRows += @@rowcount
+
+ IF @IsResourceChangeCaptureEnabled = 1 AND NOT EXISTS (SELECT * FROM dbo.Parameters WHERE Id = ''InvisibleHistory.IsEnabled'' AND Number = 0)
+ UPDATE dbo.Resource
+ SET IsHistory = 1
+ ,RawResource = 0xF -- "invisible" value
+ ,SearchParamHash = NULL
+ ,HistoryTransactionId = @TransactionId
+ WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId AND KeepHistory = 0)
+ ELSE
+ DELETE FROM dbo.Resource WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId AND KeepHistory = 0)
+ SET @AffectedRows += @@rowcount
+
+ DELETE FROM dbo.ResourceWriteClaim WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.ResourceReferenceSearchParams
+ OUTPUT deleted.ReferenceResourceTypeId, deleted.ReferenceResourceIdInt INTO @CurrentRefIdsRaw
+ WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @CurrentRows = @@rowcount
+ SET @AffectedRows += @CurrentRows
+ -- start deleting from ResourceIdIntMap
+ INSERT INTO @CurrentRefIds SELECT DISTINCT ResourceTypeId, ResourceIdInt FROM @CurrentRefIdsRaw
+ SET @CurrentRows = @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove not reused
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM @ReferenceSearchParamsWithIds B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove referenced in Resources
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove still referenced in ResourceReferenceSearchParams
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- delete from id map
+ DELETE FROM B FROM @CurrentRefIds A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap = @@rowcount
+ END
+ END
+ END
+ END
+ DELETE FROM dbo.StringReferenceSearchParams WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenText WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.StringSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.UriSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.NumberSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.QuantitySearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.DateTimeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.ReferenceTokenCompositeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenTokenCompositeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenDateTimeCompositeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenQuantityCompositeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenStringCompositeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+ DELETE FROM dbo.TokenNumberNumberCompositeSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @AffectedRows += @@rowcount
+
+ --EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Info'',@Start=@st,@Rows=@AffectedRows,@Text=''Old rows''
+ END
+
+ INSERT INTO dbo.ResourceIdIntMap
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, ResourceIdInt, ResourceId
+ FROM @InsertedIdsResource
+
+ INSERT INTO dbo.ResourceIdIntMap
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, ResourceIdInt, ResourceId
+ FROM @InsertedIdsReference
+
+ INSERT INTO dbo.Resource
+ ( ResourceTypeId, ResourceIdInt, Version, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, RawResource, IsRawResourceMetaSet, SearchParamHash, TransactionId, FileId, OffsetInFile )
+ SELECT ResourceTypeId, ResourceIdInt, Version, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, RawResource, IsRawResourceMetaSet, SearchParamHash, @TransactionId, FileId, OffsetInFile
+ FROM @ResourcesWithIds
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.ResourceWriteClaim
+ ( ResourceSurrogateId, ClaimTypeId, ClaimValue )
+ SELECT ResourceSurrogateId, ClaimTypeId, ClaimValue
+ FROM @ResourceWriteClaims
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.ResourceReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt
+ FROM @ReferenceSearchParamsWithIds
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.StringReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId
+ FROM @ReferenceSearchParams
+ WHERE ReferenceResourceTypeId IS NULL
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow
+ FROM @TokenSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenText
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Text )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Text
+ FROM @TokenTexts
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.StringSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax
+ FROM @StringSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.UriSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri
+ FROM @UriSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.NumberSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue
+ FROM @NumberSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.QuantitySearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue
+ FROM @QuantitySearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.DateTimeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax
+ FROM @DateTimeSearchParms
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.ReferenceTokenCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2
+ FROM @ReferenceTokenCompositeSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenTokenCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2
+ FROM @TokenTokenCompositeSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenDateTimeCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2
+ FROM @TokenDateTimeCompositeSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenQuantityCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2
+ FROM @TokenQuantityCompositeSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenStringCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2
+ FROM @TokenStringCompositeSearchParams
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenNumberNumberCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange
+ FROM @TokenNumberNumberCompositeSearchParams
+ SET @AffectedRows += @@rowcount
+ END -- @IsRetry = 0
+ ELSE
+ BEGIN -- @IsRetry = 1
+ INSERT INTO dbo.ResourceWriteClaim
+ ( ResourceSurrogateId, ClaimTypeId, ClaimValue )
+ SELECT ResourceSurrogateId, ClaimTypeId, ClaimValue
+ FROM (SELECT TOP (@DummyTop) * FROM @ResourceWriteClaims) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.ResourceWriteClaim C WHERE C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.ResourceReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT A.ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt
+ FROM (SELECT TOP (@DummyTop) * FROM @ReferenceSearchParamsWithIds) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.StringReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId )
+ SELECT A.ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId
+ FROM (SELECT TOP (@DummyTop) * FROM @ReferenceSearchParams WHERE ReferenceResourceTypeId IS NULL) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.StringReferenceSearchParams C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenText
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Text )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Text
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenTexts) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.StringSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax
+ FROM (SELECT TOP (@DummyTop) * FROM @StringSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenText C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.UriSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri
+ FROM (SELECT TOP (@DummyTop) * FROM @UriSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.UriSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.NumberSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue
+ FROM (SELECT TOP (@DummyTop) * FROM @NumberSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.NumberSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.QuantitySearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue
+ FROM (SELECT TOP (@DummyTop) * FROM @QuantitySearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.QuantitySearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.DateTimeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax
+ FROM (SELECT TOP (@DummyTop) * FROM @DateTimeSearchParms) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.ReferenceTokenCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2
+ FROM (SELECT TOP (@DummyTop) * FROM @ReferenceTokenCompositeSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.DateTimeSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenTokenCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenTokenCompositeSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenTokenCompositeSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenDateTimeCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenDateTimeCompositeSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenDateTimeCompositeSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenQuantityCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenQuantityCompositeSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenQuantityCompositeSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenStringCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2 )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenStringCompositeSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenStringCompositeSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.TokenNumberNumberCompositeSearchParam
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange
+ FROM (SELECT TOP (@DummyTop) * FROM @TokenNumberNumberCompositeSearchParams) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.TokenNumberNumberCompositeSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+ END
+
+ IF @IsResourceChangeCaptureEnabled = 1 --If the resource change capture feature is enabled, to execute a stored procedure called CaptureResourceChanges to insert resource change data.
+ EXECUTE dbo.CaptureResourceIdsForChanges @Resources, @ResourcesLake
+
+ IF @TransactionId IS NOT NULL
+ EXECUTE dbo.MergeResourcesCommitTransaction @TransactionId
+
+ IF @InitialTranCount = 0 AND @@trancount > 0 COMMIT TRANSACTION
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Start=@st,@Rows=@AffectedRows,@Text=@DeletedIdMap
+END TRY
+BEGIN CATCH
+ IF @InitialTranCount = 0 AND @@trancount > 0 ROLLBACK TRANSACTION
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error'',@Start=@st
+
+ IF error_number() IN (2601, 2627) AND error_message() LIKE ''%''''dbo.ResourceIdIntMap''''%'' -- pk violation
+ OR error_number() = 547 AND error_message() LIKE ''%DELETE%'' -- reference violation on DELETE
+ BEGIN
+ DELETE FROM @ResourcesWithIds
+ DELETE FROM @ReferenceSearchParamsWithIds
+ DELETE FROM @CurrentRefIdsRaw
+ DELETE FROM @CurrentRefIds
+ DELETE FROM @InputIds
+ DELETE FROM @InsertIds
+ DELETE FROM @InsertedIdsReference
+ DELETE FROM @ExistingIdsReference
+ DELETE FROM @InsertedIdsResource
+ DELETE FROM @ExistingIdsResource
+ DELETE FROM @Existing
+ DELETE FROM @ResourceInfos
+ DELETE FROM @PreviousSurrogateIds
+
+ GOTO RetryResourceIdIntMapLogic
+ END
+ ELSE
+ IF @RaiseExceptionOnConflict = 1 AND error_number() IN (2601, 2627) AND (error_message() LIKE ''%''''dbo.Resource%'' OR error_message() LIKE ''%''''dbo.CurrentResources%'' OR error_message() LIKE ''%''''dbo.HistoryResources%'' OR error_message() LIKE ''%''''dbo.RawResources''''%'')
+ THROW 50409, ''Resource has been recently updated or added, please compare the resource content in code for any duplicate updates'', 1;
+ ELSE
+ THROW
+END CATCH
+ ')
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='MergeResources',@Action='Alter'
+
+ EXECUTE('
+ALTER PROCEDURE dbo.MergeResourcesDeleteInvisibleHistory @TransactionId bigint, @AffectedRows int = NULL OUT
+AS
+set nocount on
+DECLARE @SP varchar(100) = object_name(@@procid)
+ ,@Mode varchar(100) = ''T=''+convert(varchar,@TransactionId)
+ ,@st datetime
+ ,@Rows int
+ ,@DeletedIdMap int
+
+SET @AffectedRows = 0
+
+Retry:
+BEGIN TRY
+ DECLARE @Ids TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL)
+
+ BEGIN TRANSACTION
+
+ SET @st = getUTCdate()
+ DELETE FROM A
+ OUTPUT deleted.ResourceTypeId, deleted.ResourceIdInt INTO @Ids
+ FROM dbo.Resource A
+ WHERE HistoryTransactionId = @TransactionId -- requires updated statistics
+ SET @Rows = @@rowcount
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Run'',@Target=''Resource'',@Action=''Delete'',@Start=@st,@Rows=@Rows
+ SET @AffectedRows += @Rows
+
+ SET @st = getUTCdate()
+ IF @Rows > 0
+ BEGIN
+ -- remove referenced in resources
+ DELETE FROM A FROM @Ids A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ -- remove referenced in reference search params
+ DELETE FROM A FROM @Ids A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ -- delete from id map
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap = @@rowcount
+ END
+ END
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Run'',@Target=''ResourceIdIntMap'',@Action=''Delete'',@Start=@st,@Rows=@DeletedIdMap
+ END
+
+ COMMIT TRANSACTION
+
+ SET @st = getUTCdate()
+ UPDATE dbo.Resource SET TransactionId = NULL WHERE TransactionId = @TransactionId
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Target=''Resource'',@Action=''Update'',@Start=@st,@Rows=@@rowcount
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+ IF @@trancount > 0 ROLLBACK TRANSACTION
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error''
+ IF error_number() = 547 AND error_message() LIKE ''%DELETE%'' -- reference violation on DELETE
+ BEGIN
+ DELETE FROM @Ids
+ GOTO Retry
+ END
+ ELSE
+ THROW
+END CATCH
+ ')
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='MergeResourcesDeleteInvisibleHistory',@Action='Alter'
+
+ EXECUTE('
+ALTER PROCEDURE dbo.GetResourcesByTypeAndSurrogateIdRange @ResourceTypeId smallint, @StartId bigint, @EndId bigint, @GlobalEndId bigint = NULL, @IncludeHistory bit = 1, @IncludeDeleted bit = 1
+AS
+set nocount on
+DECLARE @SP varchar(100) = ''GetResourcesByTypeAndSurrogateIdRange''
+ ,@Mode varchar(100) = ''RT=''+isnull(convert(varchar,@ResourceTypeId),''NULL'')
+ +'' S=''+isnull(convert(varchar,@StartId),''NULL'')
+ +'' E=''+isnull(convert(varchar,@EndId),''NULL'')
+ +'' GE=''+isnull(convert(varchar,@GlobalEndId),''NULL'')
+ +'' HI=''+isnull(convert(varchar,@IncludeHistory),''NULL'')
+ +'' DE=''+isnull(convert(varchar,@IncludeDeleted),''NULL'')
+ ,@st datetime = getUTCdate()
+ ,@DummyTop bigint = 9223372036854775807
+ ,@Rows int
+
+BEGIN TRY
+ DECLARE @ResourceIdInts TABLE (ResourceIdInt bigint PRIMARY KEY)
+ DECLARE @SurrogateIds TABLE (MaxSurrogateId bigint PRIMARY KEY)
+
+ IF @GlobalEndId IS NOT NULL AND @IncludeHistory = 0 -- snapshot view
+ BEGIN
+ INSERT INTO @ResourceIdInts
+ SELECT DISTINCT ResourceIdInt
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ AND IsHistory = 1
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ OPTION (MAXDOP 1)
+
+ IF @@rowcount > 0
+ INSERT INTO @SurrogateIds
+ SELECT ResourceSurrogateId
+ FROM (SELECT ResourceIdInt, ResourceSurrogateId, RowId = row_number() OVER (PARTITION BY ResourceIdInt ORDER BY ResourceSurrogateId DESC)
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceIdInt IN (SELECT TOP (@DummyTop) ResourceIdInt FROM @ResourceIdInts)
+ AND ResourceSurrogateId BETWEEN @StartId AND @GlobalEndId
+ ) A
+ WHERE RowId = 1
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ END
+
+ IF @IncludeHistory = 0
+ SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource, FileId, OffsetInFile
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ AND IsHistory = 0
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ UNION ALL
+ SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource, FileId, OffsetInFile
+ FROM @SurrogateIds
+ JOIN dbo.Resource ON ResourceTypeId = @ResourceTypeId AND ResourceSurrogateId = MaxSurrogateId
+ WHERE IsHistory = 1
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ OPTION (MAXDOP 1, LOOP JOIN)
+ ELSE -- @IncludeHistory = 1
+ SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource, FileId, OffsetInFile
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ UNION ALL
+ SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource, FileId, OffsetInFile
+ FROM @SurrogateIds
+ JOIN dbo.Resource ON ResourceTypeId = @ResourceTypeId AND ResourceSurrogateId = MaxSurrogateId
+ WHERE IsHistory = 1
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ OPTION (MAXDOP 1, LOOP JOIN)
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''End'',@Start=@st,@Rows=@@rowcount
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status=''Error'';
+ THROW
+END CATCH
+ ')
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Run',@Target='GetResourcesByTypeAndSurrogateIdRange',@Action='Alter'
+END TRY
+BEGIN CATCH
+ EXECUTE dbo.LogEvent @Process=@Process,@Status='Error';
+ THROW
+END CATCH
+GO
+--DROP TABLE IF EXISTS ResourceTbl -- TODO: Remove table after deployment
+GO
+--DROP TABLE IF EXISTS ReferenceSearchParamTbl -- TODO: Remove table after deployment
+GO
+DROP PROCEDURE IF EXISTS tmp_MoveResources
+GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Migrations/85.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Migrations/85.sql
new file mode 100644
index 0000000000..afcbf27e67
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Migrations/85.sql
@@ -0,0 +1,6473 @@
+
+/*************************************************************************************************
+ Auto-Generated from Sql build task. Do not manually edit it.
+**************************************************************************************************/
+SET XACT_ABORT ON
+BEGIN TRAN
+IF EXISTS (SELECT *
+ FROM sys.tables
+ WHERE name = 'ClaimType')
+ BEGIN
+ ROLLBACK;
+ RETURN;
+ END
+
+
+GO
+INSERT INTO dbo.SchemaVersion
+VALUES (85, 'started');
+
+CREATE PARTITION FUNCTION PartitionFunction_ResourceTypeId(SMALLINT)
+ AS RANGE RIGHT
+ FOR VALUES (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150);
+
+CREATE PARTITION SCHEME PartitionScheme_ResourceTypeId
+ AS PARTITION PartitionFunction_ResourceTypeId
+ ALL TO ([PRIMARY]);
+
+
+GO
+CREATE PARTITION FUNCTION PartitionFunction_ResourceChangeData_Timestamp(DATETIME2 (7))
+ AS RANGE RIGHT
+ FOR VALUES (N'1970-01-01T00:00:00.0000000');
+
+CREATE PARTITION SCHEME PartitionScheme_ResourceChangeData_Timestamp
+ AS PARTITION PartitionFunction_ResourceChangeData_Timestamp
+ ALL TO ([PRIMARY]);
+
+DECLARE @numberOfHistoryPartitions AS INT = 48;
+
+DECLARE @numberOfFuturePartitions AS INT = 720;
+
+DECLARE @rightPartitionBoundary AS DATETIME2 (7);
+
+DECLARE @currentDateTime AS DATETIME2 (7) = sysutcdatetime();
+
+WHILE @numberOfHistoryPartitions >= -@numberOfFuturePartitions
+ BEGIN
+ SET @rightPartitionBoundary = DATEADD(hour, DATEDIFF(hour, 0, @currentDateTime) - @numberOfHistoryPartitions, 0);
+ ALTER PARTITION SCHEME PartitionScheme_ResourceChangeData_Timestamp NEXT USED [Primary];
+ ALTER PARTITION FUNCTION PartitionFunction_ResourceChangeData_Timestamp( )
+ SPLIT RANGE (@rightPartitionBoundary);
+ SET @numberOfHistoryPartitions -= 1;
+ END
+
+CREATE SEQUENCE dbo.ResourceSurrogateIdUniquifierSequence
+ AS INT
+ START WITH 0
+ INCREMENT BY 1
+ MINVALUE 0
+ MAXVALUE 79999
+ CYCLE
+ CACHE 1000000;
+
+
+GO
+CREATE SEQUENCE dbo.ResourceIdIntMapSequence
+ AS INT
+ START WITH 0
+ INCREMENT BY 1
+ MINVALUE 0
+ MAXVALUE 79999
+ CYCLE
+ CACHE 1000000;
+
+CREATE TYPE dbo.BigintList AS TABLE (
+ Id BIGINT NOT NULL PRIMARY KEY);
+
+CREATE TYPE dbo.DateTimeSearchParamList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ StartDateTime DATETIMEOFFSET (7) NOT NULL,
+ EndDateTime DATETIMEOFFSET (7) NOT NULL,
+ IsLongerThanADay BIT NOT NULL,
+ IsMin BIT NOT NULL,
+ IsMax BIT NOT NULL UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax));
+
+CREATE TYPE dbo.NumberSearchParamList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SingleValue DECIMAL (36, 18) NULL,
+ LowValue DECIMAL (36, 18) NULL,
+ HighValue DECIMAL (36, 18) NULL UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue));
+
+CREATE TYPE dbo.QuantitySearchParamList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId INT NULL,
+ QuantityCodeId INT NULL,
+ SingleValue DECIMAL (36, 18) NULL,
+ LowValue DECIMAL (36, 18) NULL,
+ HighValue DECIMAL (36, 18) NULL UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue));
+
+CREATE TYPE dbo.ReferenceSearchParamList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ BaseUri VARCHAR (128) COLLATE Latin1_General_100_CS_AS NULL,
+ ReferenceResourceTypeId SMALLINT NULL,
+ ReferenceResourceId VARCHAR (768) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ ReferenceResourceVersion INT NULL UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceId));
+
+CREATE TYPE dbo.ReferenceTokenCompositeSearchParamList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ BaseUri1 VARCHAR (128) COLLATE Latin1_General_100_CS_AS NULL,
+ ReferenceResourceTypeId1 SMALLINT NULL,
+ ReferenceResourceId1 VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ ReferenceResourceVersion1 INT NULL,
+ SystemId2 INT NULL,
+ Code2 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CodeOverflow2 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL);
+
+CREATE TYPE dbo.ResourceDateKeyList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId, ResourceSurrogateId));
+
+CREATE TYPE dbo.ResourceKeyList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ Version INT NULL UNIQUE (ResourceTypeId, ResourceId, Version));
+
+CREATE TYPE dbo.ResourceList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ Version INT NOT NULL,
+ HasVersionToCompare BIT NOT NULL,
+ IsDeleted BIT NOT NULL,
+ IsHistory BIT NOT NULL,
+ KeepHistory BIT NOT NULL,
+ RawResource VARBINARY (MAX) NOT NULL,
+ IsRawResourceMetaSet BIT NOT NULL,
+ RequestMethod VARCHAR (10) NULL,
+ SearchParamHash VARCHAR (64) NULL PRIMARY KEY (ResourceTypeId, ResourceSurrogateId),
+ UNIQUE (ResourceTypeId, ResourceId, Version));
+
+CREATE TYPE dbo.ResourceListLake AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ Version INT NOT NULL,
+ HasVersionToCompare BIT NOT NULL,
+ IsDeleted BIT NOT NULL,
+ IsHistory BIT NOT NULL,
+ KeepHistory BIT NOT NULL,
+ RawResource VARBINARY (MAX) NULL,
+ IsRawResourceMetaSet BIT NOT NULL,
+ RequestMethod VARCHAR (10) NULL,
+ SearchParamHash VARCHAR (64) NULL,
+ FileId BIGINT NULL,
+ OffsetInFile INT NULL PRIMARY KEY (ResourceTypeId, ResourceSurrogateId),
+ UNIQUE (ResourceTypeId, ResourceId, Version));
+
+CREATE TYPE dbo.ResourceWriteClaimList AS TABLE (
+ ResourceSurrogateId BIGINT NOT NULL,
+ ClaimTypeId TINYINT NOT NULL,
+ ClaimValue NVARCHAR (128) NOT NULL);
+
+CREATE TYPE dbo.StringList AS TABLE (
+ String VARCHAR (MAX));
+
+CREATE TYPE dbo.StringSearchParamList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ Text NVARCHAR (256) COLLATE Latin1_General_100_CI_AI_SC NOT NULL,
+ TextOverflow NVARCHAR (MAX) COLLATE Latin1_General_100_CI_AI_SC NULL,
+ IsMin BIT NOT NULL,
+ IsMax BIT NOT NULL);
+
+CREATE TYPE dbo.TokenDateTimeCompositeSearchParamList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId1 INT NULL,
+ Code1 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CodeOverflow1 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL,
+ StartDateTime2 DATETIMEOFFSET (7) NOT NULL,
+ EndDateTime2 DATETIMEOFFSET (7) NOT NULL,
+ IsLongerThanADay2 BIT NOT NULL);
+
+CREATE TYPE dbo.TokenNumberNumberCompositeSearchParamList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId1 INT NULL,
+ Code1 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CodeOverflow1 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL,
+ SingleValue2 DECIMAL (36, 18) NULL,
+ LowValue2 DECIMAL (36, 18) NULL,
+ HighValue2 DECIMAL (36, 18) NULL,
+ SingleValue3 DECIMAL (36, 18) NULL,
+ LowValue3 DECIMAL (36, 18) NULL,
+ HighValue3 DECIMAL (36, 18) NULL,
+ HasRange BIT NOT NULL);
+
+CREATE TYPE dbo.TokenQuantityCompositeSearchParamList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId1 INT NULL,
+ Code1 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CodeOverflow1 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL,
+ SystemId2 INT NULL,
+ QuantityCodeId2 INT NULL,
+ SingleValue2 DECIMAL (36, 18) NULL,
+ LowValue2 DECIMAL (36, 18) NULL,
+ HighValue2 DECIMAL (36, 18) NULL);
+
+CREATE TYPE dbo.TokenSearchParamList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId INT NULL,
+ Code VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CodeOverflow VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL);
+
+CREATE TYPE dbo.TokenStringCompositeSearchParamList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId1 INT NULL,
+ Code1 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CodeOverflow1 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL,
+ Text2 NVARCHAR (256) COLLATE Latin1_General_100_CI_AI_SC NOT NULL,
+ TextOverflow2 NVARCHAR (MAX) COLLATE Latin1_General_100_CI_AI_SC NULL);
+
+CREATE TYPE dbo.TokenTextList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ Text NVARCHAR (400) COLLATE Latin1_General_CI_AI NOT NULL);
+
+CREATE TYPE dbo.TokenTokenCompositeSearchParamList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId1 INT NULL,
+ Code1 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CodeOverflow1 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL,
+ SystemId2 INT NULL,
+ Code2 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CodeOverflow2 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL);
+
+CREATE TYPE dbo.SearchParamTableType_2 AS TABLE (
+ Uri VARCHAR (128) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ Status VARCHAR (20) NOT NULL,
+ IsPartiallySupported BIT NOT NULL);
+
+CREATE TYPE dbo.BulkReindexResourceTableType_1 AS TABLE (
+ Offset INT NOT NULL,
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ ETag INT NULL,
+ SearchParamHash VARCHAR (64) NOT NULL);
+
+CREATE TYPE dbo.UriSearchParamList AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ Uri VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri));
+
+CREATE TABLE dbo.CurrentResource (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ ResourceIdInt BIGINT NOT NULL,
+ Version INT NOT NULL,
+ IsHistory BIT NOT NULL,
+ IsDeleted BIT NOT NULL,
+ RequestMethod VARCHAR (10) NULL,
+ RawResource VARBINARY (MAX) NULL,
+ IsRawResourceMetaSet BIT NOT NULL,
+ SearchParamHash VARCHAR (64) NULL,
+ TransactionId BIGINT NULL,
+ HistoryTransactionId BIGINT NULL,
+ FileId BIGINT NULL,
+ OffsetInFile INT NULL
+);
+
+
+GO
+DROP TABLE dbo.CurrentResource;
+
+
+GO
+CREATE TABLE dbo.Resource (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ ResourceIdInt BIGINT NOT NULL,
+ Version INT NOT NULL,
+ IsHistory BIT NOT NULL,
+ IsDeleted BIT NOT NULL,
+ RequestMethod VARCHAR (10) NULL,
+ RawResource VARBINARY (MAX) NULL,
+ IsRawResourceMetaSet BIT NOT NULL,
+ SearchParamHash VARCHAR (64) NULL,
+ TransactionId BIGINT NULL,
+ HistoryTransactionId BIGINT NULL,
+ FileId BIGINT NULL,
+ OffsetInFile INT NULL
+);
+
+CREATE INDEX IX_Resource_ResourceTypeId_ResourceSurrgateId
+ ON Resource(ResourceTypeId);
+
+
+GO
+DROP TABLE dbo.Resource;
+
+
+GO
+CREATE TABLE dbo.ResourceIdIntMap (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL CONSTRAINT PKC_ResourceIdIntMap_ResourceIdInt_ResourceTypeId PRIMARY KEY CLUSTERED (ResourceIdInt, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId),
+ CONSTRAINT U_ResourceIdIntMap_ResourceId_ResourceTypeId UNIQUE (ResourceId, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+);
+
+ALTER TABLE dbo.ResourceIdIntMap SET (LOCK_ESCALATION = AUTO);
+
+
+GO
+CREATE TABLE dbo.RawResources (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ RawResource VARBINARY (MAX) NULL CONSTRAINT PKC_RawResources_ResourceTypeId_ResourceSurrogateId PRIMARY KEY CLUSTERED (ResourceTypeId, ResourceSurrogateId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+);
+
+ALTER TABLE dbo.RawResources SET (LOCK_ESCALATION = AUTO);
+
+
+GO
+CREATE TABLE dbo.CurrentResources (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL,
+ Version INT NOT NULL,
+ IsHistory BIT CONSTRAINT DF_CurrentResources_IsHistory DEFAULT 0 NOT NULL,
+ IsDeleted BIT NOT NULL,
+ RequestMethod VARCHAR (10) NULL,
+ IsRawResourceMetaSet BIT CONSTRAINT DF_CurrentResources_IsRawResourceMetaSet DEFAULT 0 NOT NULL,
+ SearchParamHash VARCHAR (64) NULL,
+ TransactionId BIGINT NULL,
+ HistoryTransactionId BIGINT NULL,
+ FileId BIGINT NULL,
+ OffsetInFile INT NULL CONSTRAINT PKC_CurrentResources_ResourceTypeId_ResourceSurrogateId PRIMARY KEY CLUSTERED (ResourceTypeId, ResourceSurrogateId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId),
+ CONSTRAINT CH_CurrentResources_IsHistory CHECK (IsHistory = 0),
+ CONSTRAINT U_CurrentResources_ResourceIdInt_ResourceTypeId UNIQUE (ResourceIdInt, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+);
+
+ALTER TABLE dbo.CurrentResources
+ ADD CONSTRAINT FK_CurrentResources_ResourceIdInt_ResourceTypeId_ResourceIdIntMap FOREIGN KEY (ResourceIdInt, ResourceTypeId) REFERENCES dbo.ResourceIdIntMap (ResourceIdInt, ResourceTypeId);
+
+ALTER TABLE dbo.CurrentResources SET (LOCK_ESCALATION = AUTO);
+
+CREATE INDEX IX_TransactionId_ResourceTypeId_WHERE_TransactionId_NOT_NULL
+ ON dbo.CurrentResources(TransactionId, ResourceTypeId) WHERE TransactionId IS NOT NULL WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_HistoryTransactionId_ResourceTypeId_WHERE_HistoryTransactionId_NOT_NULL
+ ON dbo.CurrentResources(HistoryTransactionId, ResourceTypeId) WHERE HistoryTransactionId IS NOT NULL WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+
+GO
+CREATE TABLE dbo.HistoryResources (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL,
+ Version INT NOT NULL,
+ IsHistory BIT CONSTRAINT DF_HistoryResources_IsHistory DEFAULT 1 NOT NULL,
+ IsDeleted BIT NOT NULL,
+ RequestMethod VARCHAR (10) NULL,
+ IsRawResourceMetaSet BIT CONSTRAINT DF_HistoryResources_IsRawResourceMetaSet DEFAULT 0 NOT NULL,
+ SearchParamHash VARCHAR (64) NULL,
+ TransactionId BIGINT NULL,
+ HistoryTransactionId BIGINT NULL,
+ FileId BIGINT NULL,
+ OffsetInFile INT NULL CONSTRAINT PKC_HistoryResources_ResourceTypeId_ResourceSurrogateId PRIMARY KEY CLUSTERED (ResourceTypeId, ResourceSurrogateId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId),
+ CONSTRAINT CH_HistoryResources_IsHistory CHECK (IsHistory = 1),
+ CONSTRAINT U_HistoryResources_ResourceIdInt_Version_ResourceTypeId UNIQUE (ResourceIdInt, Version, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+);
+
+ALTER TABLE dbo.HistoryResources
+ ADD CONSTRAINT FK_HistoryResources_ResourceIdInt_ResourceTypeId_ResourceIdIntMap FOREIGN KEY (ResourceIdInt, ResourceTypeId) REFERENCES dbo.ResourceIdIntMap (ResourceIdInt, ResourceTypeId);
+
+ALTER TABLE dbo.HistoryResources SET (LOCK_ESCALATION = AUTO);
+
+CREATE INDEX IX_TransactionId_ResourceTypeId_WHERE_TransactionId_NOT_NULL
+ ON dbo.HistoryResources(TransactionId, ResourceTypeId) WHERE TransactionId IS NOT NULL WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_HistoryTransactionId_ResourceTypeId_WHERE_HistoryTransactionId_NOT_NULL
+ ON dbo.HistoryResources(HistoryTransactionId, ResourceTypeId) WHERE HistoryTransactionId IS NOT NULL WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.ClaimType (
+ ClaimTypeId TINYINT IDENTITY (1, 1) NOT NULL,
+ Name VARCHAR (128) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CONSTRAINT UQ_ClaimType_ClaimTypeId UNIQUE (ClaimTypeId),
+ CONSTRAINT PKC_ClaimType PRIMARY KEY CLUSTERED (Name) WITH (DATA_COMPRESSION = PAGE)
+);
+
+CREATE TABLE dbo.CompartmentAssignment (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ CompartmentTypeId TINYINT NOT NULL,
+ ReferenceResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ IsHistory BIT NOT NULL,
+ CONSTRAINT PKC_CompartmentAssignment PRIMARY KEY CLUSTERED (ResourceTypeId, ResourceSurrogateId, CompartmentTypeId, ReferenceResourceId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+);
+
+
+GO
+ALTER TABLE dbo.CompartmentAssignment
+ ADD CONSTRAINT DF_CompartmentAssignment_IsHistory DEFAULT 0 FOR IsHistory;
+
+
+GO
+ALTER TABLE dbo.CompartmentAssignment SET (LOCK_ESCALATION = AUTO);
+
+
+GO
+CREATE NONCLUSTERED INDEX IX_CompartmentAssignment_CompartmentTypeId_ReferenceResourceId
+ ON dbo.CompartmentAssignment(ResourceTypeId, CompartmentTypeId, ReferenceResourceId, ResourceSurrogateId) WHERE IsHistory = 0 WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.CompartmentType (
+ CompartmentTypeId TINYINT IDENTITY (1, 1) NOT NULL,
+ Name VARCHAR (128) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CONSTRAINT UQ_CompartmentType_CompartmentTypeId UNIQUE (CompartmentTypeId),
+ CONSTRAINT PKC_CompartmentType PRIMARY KEY CLUSTERED (Name) WITH (DATA_COMPRESSION = PAGE)
+);
+
+CREATE TABLE dbo.DateTimeSearchParam (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ StartDateTime DATETIME2 (7) NOT NULL,
+ EndDateTime DATETIME2 (7) NOT NULL,
+ IsLongerThanADay BIT NOT NULL,
+ IsMin BIT CONSTRAINT date_IsMin_Constraint DEFAULT 0 NOT NULL,
+ IsMax BIT CONSTRAINT date_IsMax_Constraint DEFAULT 0 NOT NULL
+);
+
+ALTER TABLE dbo.DateTimeSearchParam SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_DateTimeSearchParam
+ ON dbo.DateTimeSearchParam(ResourceTypeId, ResourceSurrogateId, SearchParamId)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_StartDateTime_EndDateTime_INCLUDE_IsLongerThanADay_IsMin_IsMax
+ ON dbo.DateTimeSearchParam(SearchParamId, StartDateTime, EndDateTime)
+ INCLUDE(IsLongerThanADay, IsMin, IsMax)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_EndDateTime_StartDateTime_INCLUDE_IsLongerThanADay_IsMin_IsMax
+ ON dbo.DateTimeSearchParam(SearchParamId, EndDateTime, StartDateTime)
+ INCLUDE(IsLongerThanADay, IsMin, IsMax)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_StartDateTime_EndDateTime_INCLUDE_IsMin_IsMax_WHERE_IsLongerThanADay_1
+ ON dbo.DateTimeSearchParam(SearchParamId, StartDateTime, EndDateTime)
+ INCLUDE(IsMin, IsMax) WHERE IsLongerThanADay = 1
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_EndDateTime_StartDateTime_INCLUDE_IsMin_IsMax_WHERE_IsLongerThanADay_1
+ ON dbo.DateTimeSearchParam(SearchParamId, EndDateTime, StartDateTime)
+ INCLUDE(IsMin, IsMax) WHERE IsLongerThanADay = 1
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+IF NOT EXISTS (SELECT 1
+ FROM sys.tables
+ WHERE name = 'EventAgentCheckpoint')
+ BEGIN
+ CREATE TABLE dbo.EventAgentCheckpoint (
+ CheckpointId VARCHAR (64) NOT NULL,
+ LastProcessedDateTime DATETIMEOFFSET (7),
+ LastProcessedIdentifier VARCHAR (64) ,
+ UpdatedOn DATETIME2 (7) DEFAULT sysutcdatetime() NOT NULL,
+ CONSTRAINT PK_EventAgentCheckpoint PRIMARY KEY CLUSTERED (CheckpointId)
+ ) ON [PRIMARY];
+ END
+
+CREATE PARTITION FUNCTION EventLogPartitionFunction(TINYINT)
+ AS RANGE RIGHT
+ FOR VALUES (0, 1, 2, 3, 4, 5, 6, 7);
+
+
+GO
+CREATE PARTITION SCHEME EventLogPartitionScheme
+ AS PARTITION EventLogPartitionFunction
+ ALL TO ([PRIMARY]);
+
+
+GO
+CREATE TABLE dbo.EventLog (
+ PartitionId AS isnull(CONVERT (TINYINT, EventId % 8), 0) PERSISTED,
+ EventId BIGINT IDENTITY (1, 1) NOT NULL,
+ EventDate DATETIME NOT NULL,
+ Process VARCHAR (100) NOT NULL,
+ Status VARCHAR (10) NOT NULL,
+ Mode VARCHAR (200) NULL,
+ Action VARCHAR (20) NULL,
+ Target VARCHAR (100) NULL,
+ Rows BIGINT NULL,
+ Milliseconds INT NULL,
+ EventText NVARCHAR (3500) NULL,
+ SPID SMALLINT NOT NULL,
+ HostName VARCHAR (64) NOT NULL CONSTRAINT PKC_EventLog_EventDate_EventId_PartitionId PRIMARY KEY CLUSTERED (EventDate, EventId, PartitionId) ON EventLogPartitionScheme (PartitionId)
+);
+
+CREATE TABLE dbo.ExportJob (
+ Id VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ Hash VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ Status VARCHAR (10) NOT NULL,
+ HeartbeatDateTime DATETIME2 (7) NULL,
+ RawJobRecord VARCHAR (MAX) NOT NULL,
+ JobVersion ROWVERSION NOT NULL,
+ CONSTRAINT PKC_ExportJob PRIMARY KEY CLUSTERED (Id)
+);
+
+CREATE UNIQUE NONCLUSTERED INDEX IX_ExportJob_Hash_Status_HeartbeatDateTime
+ ON dbo.ExportJob(Hash, Status, HeartbeatDateTime);
+
+CREATE TABLE dbo.IndexProperties (
+ TableName VARCHAR (100) NOT NULL,
+ IndexName VARCHAR (200) NOT NULL,
+ PropertyName VARCHAR (100) NOT NULL,
+ PropertyValue VARCHAR (100) NOT NULL,
+ CreateDate DATETIME CONSTRAINT DF_IndexProperties_CreateDate DEFAULT getUTCdate() NOT NULL CONSTRAINT PKC_IndexProperties_TableName_IndexName_PropertyName PRIMARY KEY CLUSTERED (TableName, IndexName, PropertyName)
+);
+
+CREATE PARTITION FUNCTION TinyintPartitionFunction(TINYINT)
+ AS RANGE RIGHT
+ FOR VALUES (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255);
+
+
+GO
+CREATE PARTITION SCHEME TinyintPartitionScheme
+ AS PARTITION TinyintPartitionFunction
+ ALL TO ([PRIMARY]);
+
+
+GO
+CREATE TABLE dbo.JobQueue (
+ QueueType TINYINT NOT NULL,
+ GroupId BIGINT NOT NULL,
+ JobId BIGINT NOT NULL,
+ PartitionId AS CONVERT (TINYINT, JobId % 16) PERSISTED,
+ Definition VARCHAR (MAX) NOT NULL,
+ DefinitionHash VARBINARY (20) NOT NULL,
+ Version BIGINT CONSTRAINT DF_JobQueue_Version DEFAULT datediff_big(millisecond, '0001-01-01', getUTCdate()) NOT NULL,
+ Status TINYINT CONSTRAINT DF_JobQueue_Status DEFAULT 0 NOT NULL,
+ Priority TINYINT CONSTRAINT DF_JobQueue_Priority DEFAULT 100 NOT NULL,
+ Data BIGINT NULL,
+ Result VARCHAR (MAX) NULL,
+ CreateDate DATETIME CONSTRAINT DF_JobQueue_CreateDate DEFAULT getUTCdate() NOT NULL,
+ StartDate DATETIME NULL,
+ EndDate DATETIME NULL,
+ HeartbeatDate DATETIME CONSTRAINT DF_JobQueue_HeartbeatDate DEFAULT getUTCdate() NOT NULL,
+ Worker VARCHAR (100) NULL,
+ Info VARCHAR (1000) NULL,
+ CancelRequested BIT CONSTRAINT DF_JobQueue_CancelRequested DEFAULT 0 NOT NULL CONSTRAINT PKC_JobQueue_QueueType_PartitionId_JobId PRIMARY KEY CLUSTERED (QueueType, PartitionId, JobId) ON TinyintPartitionScheme (QueueType),
+ CONSTRAINT U_JobQueue_QueueType_JobId UNIQUE (QueueType, JobId)
+);
+
+
+GO
+CREATE INDEX IX_QueueType_PartitionId_Status_Priority
+ ON dbo.JobQueue(PartitionId, Status, Priority)
+ ON TinyintPartitionScheme (QueueType);
+
+
+GO
+CREATE INDEX IX_QueueType_GroupId
+ ON dbo.JobQueue(QueueType, GroupId)
+ ON TinyintPartitionScheme (QueueType);
+
+
+GO
+CREATE INDEX IX_QueueType_DefinitionHash
+ ON dbo.JobQueue(QueueType, DefinitionHash)
+ ON TinyintPartitionScheme (QueueType);
+
+CREATE TABLE dbo.NumberSearchParam (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SingleValue DECIMAL (36, 18) NULL,
+ LowValue DECIMAL (36, 18) NOT NULL,
+ HighValue DECIMAL (36, 18) NOT NULL
+);
+
+ALTER TABLE dbo.NumberSearchParam SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_NumberSearchParam
+ ON dbo.NumberSearchParam(ResourceTypeId, ResourceSurrogateId, SearchParamId)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_SingleValue_WHERE_SingleValue_NOT_NULL
+ ON dbo.NumberSearchParam(SearchParamId, SingleValue) WHERE SingleValue IS NOT NULL
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_LowValue_HighValue
+ ON dbo.NumberSearchParam(SearchParamId, LowValue, HighValue)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_HighValue_LowValue
+ ON dbo.NumberSearchParam(SearchParamId, HighValue, LowValue)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.Parameters (
+ Id VARCHAR (100) NOT NULL,
+ Date DATETIME NULL,
+ Number FLOAT NULL,
+ Bigint BIGINT NULL,
+ Char VARCHAR (4000) NULL,
+ Binary VARBINARY (MAX) NULL,
+ UpdatedDate DATETIME NULL,
+ UpdatedBy NVARCHAR (255) NULL CONSTRAINT PKC_Parameters_Id PRIMARY KEY CLUSTERED (Id) WITH (IGNORE_DUP_KEY = ON)
+);
+
+
+GO
+CREATE TABLE dbo.ParametersHistory (
+ ChangeId INT IDENTITY (1, 1) NOT NULL,
+ Id VARCHAR (100) NOT NULL,
+ Date DATETIME NULL,
+ Number FLOAT NULL,
+ Bigint BIGINT NULL,
+ Char VARCHAR (4000) NULL,
+ Binary VARBINARY (MAX) NULL,
+ UpdatedDate DATETIME NULL,
+ UpdatedBy NVARCHAR (255) NULL
+);
+
+CREATE TABLE dbo.QuantityCode (
+ QuantityCodeId INT IDENTITY (1, 1) NOT NULL,
+ Value NVARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CONSTRAINT UQ_QuantityCode_QuantityCodeId UNIQUE (QuantityCodeId),
+ CONSTRAINT PKC_QuantityCode PRIMARY KEY CLUSTERED (Value) WITH (DATA_COMPRESSION = PAGE)
+);
+
+CREATE TABLE dbo.QuantitySearchParam (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId INT NULL,
+ QuantityCodeId INT NULL,
+ SingleValue DECIMAL (36, 18) NULL,
+ LowValue DECIMAL (36, 18) NOT NULL,
+ HighValue DECIMAL (36, 18) NOT NULL
+);
+
+ALTER TABLE dbo.QuantitySearchParam SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_QuantitySearchParam
+ ON dbo.QuantitySearchParam(ResourceTypeId, ResourceSurrogateId, SearchParamId)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_QuantityCodeId_SingleValue_INCLUDE_SystemId_WHERE_SingleValue_NOT_NULL
+ ON dbo.QuantitySearchParam(SearchParamId, QuantityCodeId, SingleValue)
+ INCLUDE(SystemId) WHERE SingleValue IS NOT NULL
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_QuantityCodeId_LowValue_HighValue_INCLUDE_SystemId
+ ON dbo.QuantitySearchParam(SearchParamId, QuantityCodeId, LowValue, HighValue)
+ INCLUDE(SystemId)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_QuantityCodeId_HighValue_LowValue_INCLUDE_SystemId
+ ON dbo.QuantitySearchParam(SearchParamId, QuantityCodeId, HighValue, LowValue)
+ INCLUDE(SystemId)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.ReferenceSearchParam (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ BaseUri VARCHAR (128) COLLATE Latin1_General_100_CS_AS NULL,
+ ReferenceResourceTypeId SMALLINT NULL,
+ ReferenceResourceId VARCHAR (768) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ ReferenceResourceIdInt BIGINT NOT NULL,
+ IsResourceRef BIT NOT NULL
+);
+
+
+GO
+DROP TABLE dbo.ReferenceSearchParam;
+
+
+GO
+CREATE TABLE dbo.ResourceReferenceSearchParams (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ BaseUri VARCHAR (128) COLLATE Latin1_General_100_CS_AS NULL,
+ ReferenceResourceTypeId SMALLINT NOT NULL,
+ ReferenceResourceIdInt BIGINT NOT NULL,
+ IsResourceRef BIT CONSTRAINT DF_ResourceReferenceSearchParams_IsResourceRef DEFAULT 1 NOT NULL,
+ CONSTRAINT CH_ResourceReferenceSearchParams_IsResourceRef CHECK (IsResourceRef = 1)
+);
+
+ALTER TABLE dbo.ResourceReferenceSearchParams
+ ADD CONSTRAINT FK_ResourceReferenceSearchParams_ReferenceResourceIdInt_ReferenceResourceTypeId_ResourceIdIntMap FOREIGN KEY (ReferenceResourceIdInt, ReferenceResourceTypeId) REFERENCES dbo.ResourceIdIntMap (ResourceIdInt, ResourceTypeId);
+
+ALTER TABLE dbo.ResourceReferenceSearchParams SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_ResourceSurrogateId_SearchParamId_ResourceTypeId
+ ON dbo.ResourceReferenceSearchParams(ResourceSurrogateId, SearchParamId, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE UNIQUE INDEX IXU_ReferenceResourceIdInt_ReferenceResourceTypeId_SearchParamId_BaseUri_ResourceSurrogateId_ResourceTypeId
+ ON dbo.ResourceReferenceSearchParams(ReferenceResourceIdInt, ReferenceResourceTypeId, SearchParamId, BaseUri, ResourceSurrogateId, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+
+GO
+CREATE TABLE dbo.StringReferenceSearchParams (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ BaseUri VARCHAR (128) COLLATE Latin1_General_100_CS_AS NULL,
+ ReferenceResourceId VARCHAR (768) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ IsResourceRef BIT CONSTRAINT DF_StringReferenceSearchParams_IsResourceRef DEFAULT 0 NOT NULL,
+ CONSTRAINT CH_StringReferenceSearchParams_IsResourceRef CHECK (IsResourceRef = 0)
+);
+
+ALTER TABLE dbo.StringReferenceSearchParams SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_ResourceSurrogateId_SearchParamId_ResourceTypeId
+ ON dbo.StringReferenceSearchParams(ResourceSurrogateId, SearchParamId, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE UNIQUE INDEX IXU_ReferenceResourceId_SearchParamId_BaseUri_ResourceSurrogateId_ResourceTypeId
+ ON dbo.StringReferenceSearchParams(ReferenceResourceId, SearchParamId, BaseUri, ResourceSurrogateId, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.ReferenceTokenCompositeSearchParam (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ BaseUri1 VARCHAR (128) COLLATE Latin1_General_100_CS_AS NULL,
+ ReferenceResourceTypeId1 SMALLINT NULL,
+ ReferenceResourceId1 VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ ReferenceResourceVersion1 INT NULL,
+ SystemId2 INT NULL,
+ Code2 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CodeOverflow2 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL
+);
+
+ALTER TABLE dbo.ReferenceTokenCompositeSearchParam
+ ADD CONSTRAINT CHK_ReferenceTokenCompositeSearchParam_CodeOverflow2 CHECK (LEN(Code2) = 256
+ OR CodeOverflow2 IS NULL);
+
+ALTER TABLE dbo.ReferenceTokenCompositeSearchParam SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_ReferenceTokenCompositeSearchParam
+ ON dbo.ReferenceTokenCompositeSearchParam(ResourceTypeId, ResourceSurrogateId, SearchParamId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_ReferenceResourceId1_Code2_INCLUDE_ReferenceResourceTypeId1_BaseUri1_SystemId2
+ ON dbo.ReferenceTokenCompositeSearchParam(SearchParamId, ReferenceResourceId1, Code2)
+ INCLUDE(ReferenceResourceTypeId1, BaseUri1, SystemId2) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.ReindexJob (
+ Id VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ Status VARCHAR (10) NOT NULL,
+ HeartbeatDateTime DATETIME2 (7) NULL,
+ RawJobRecord VARCHAR (MAX) NOT NULL,
+ JobVersion ROWVERSION NOT NULL,
+ CONSTRAINT PKC_ReindexJob PRIMARY KEY CLUSTERED (Id)
+);
+
+CREATE TABLE dbo.ResourceChangeData (
+ Id BIGINT IDENTITY (1, 1) NOT NULL,
+ Timestamp DATETIME2 (7) CONSTRAINT DF_ResourceChangeData_Timestamp DEFAULT sysutcdatetime() NOT NULL,
+ ResourceId VARCHAR (64) NOT NULL,
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceVersion INT NOT NULL,
+ ResourceChangeTypeId TINYINT NOT NULL
+) ON PartitionScheme_ResourceChangeData_Timestamp (Timestamp);
+
+CREATE CLUSTERED INDEX IXC_ResourceChangeData
+ ON dbo.ResourceChangeData(Id ASC) WITH (ONLINE = ON)
+ ON PartitionScheme_ResourceChangeData_Timestamp (Timestamp);
+
+CREATE TABLE dbo.ResourceChangeDataStaging (
+ Id BIGINT IDENTITY (1, 1) NOT NULL,
+ Timestamp DATETIME2 (7) CONSTRAINT DF_ResourceChangeDataStaging_Timestamp DEFAULT sysutcdatetime() NOT NULL,
+ ResourceId VARCHAR (64) NOT NULL,
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceVersion INT NOT NULL,
+ ResourceChangeTypeId TINYINT NOT NULL
+) ON [PRIMARY];
+
+CREATE CLUSTERED INDEX IXC_ResourceChangeDataStaging
+ ON dbo.ResourceChangeDataStaging(Id ASC, Timestamp ASC) WITH (ONLINE = ON)
+ ON [PRIMARY];
+
+ALTER TABLE dbo.ResourceChangeDataStaging WITH CHECK
+ ADD CONSTRAINT CHK_ResourceChangeDataStaging_partition CHECK (Timestamp < CONVERT (DATETIME2 (7), N'9999-12-31 23:59:59.9999999'));
+
+ALTER TABLE dbo.ResourceChangeDataStaging CHECK CONSTRAINT CHK_ResourceChangeDataStaging_partition;
+
+CREATE TABLE dbo.ResourceChangeType (
+ ResourceChangeTypeId TINYINT NOT NULL,
+ Name NVARCHAR (50) NOT NULL,
+ CONSTRAINT PK_ResourceChangeType PRIMARY KEY CLUSTERED (ResourceChangeTypeId),
+ CONSTRAINT UQ_ResourceChangeType_Name UNIQUE NONCLUSTERED (Name)
+) ON [PRIMARY];
+
+
+GO
+INSERT dbo.ResourceChangeType (ResourceChangeTypeId, Name)
+VALUES (0, N'Creation');
+
+INSERT dbo.ResourceChangeType (ResourceChangeTypeId, Name)
+VALUES (1, N'Update');
+
+INSERT dbo.ResourceChangeType (ResourceChangeTypeId, Name)
+VALUES (2, N'Deletion');
+
+CREATE TABLE dbo.ResourceType (
+ ResourceTypeId SMALLINT IDENTITY (1, 1) NOT NULL,
+ Name NVARCHAR (50) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CONSTRAINT UQ_ResourceType_ResourceTypeId UNIQUE (ResourceTypeId),
+ CONSTRAINT PKC_ResourceType PRIMARY KEY CLUSTERED (Name) WITH (DATA_COMPRESSION = PAGE)
+);
+
+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_ResourceWriteClaim
+ ON dbo.ResourceWriteClaim(ResourceSurrogateId, ClaimTypeId);
+
+CREATE TABLE dbo.SchemaMigrationProgress (
+ Timestamp DATETIME2 (3) DEFAULT CURRENT_TIMESTAMP,
+ Message NVARCHAR (MAX)
+);
+
+CREATE TABLE dbo.SearchParam (
+ SearchParamId SMALLINT IDENTITY (1, 1) NOT NULL,
+ Uri VARCHAR (128) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ Status VARCHAR (20) NULL,
+ LastUpdated DATETIMEOFFSET (7) NULL,
+ IsPartiallySupported BIT NULL,
+ CONSTRAINT UQ_SearchParam_SearchParamId UNIQUE (SearchParamId),
+ CONSTRAINT PKC_SearchParam PRIMARY KEY CLUSTERED (Uri) WITH (DATA_COMPRESSION = PAGE)
+);
+
+CREATE TABLE dbo.StringSearchParam (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ Text NVARCHAR (256) COLLATE Latin1_General_100_CI_AI_SC NOT NULL,
+ TextOverflow NVARCHAR (MAX) COLLATE Latin1_General_100_CI_AI_SC NULL,
+ IsMin BIT CONSTRAINT string_IsMin_Constraint DEFAULT 0 NOT NULL,
+ IsMax BIT CONSTRAINT string_IsMax_Constraint DEFAULT 0 NOT NULL
+);
+
+ALTER TABLE dbo.StringSearchParam SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_StringSearchParam
+ ON dbo.StringSearchParam(ResourceTypeId, ResourceSurrogateId, SearchParamId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Text_INCLUDE_TextOverflow_IsMin_IsMax
+ ON dbo.StringSearchParam(SearchParamId, Text)
+ INCLUDE(TextOverflow, IsMin, IsMax) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Text_INCLUDE_IsMin_IsMax_WHERE_TextOverflow_NOT_NULL
+ ON dbo.StringSearchParam(SearchParamId, Text)
+ INCLUDE(IsMin, IsMax) WHERE TextOverflow IS NOT NULL WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.System (
+ SystemId INT IDENTITY (1, 1) NOT NULL,
+ Value NVARCHAR (256) NOT NULL,
+ CONSTRAINT UQ_System_SystemId UNIQUE (SystemId),
+ CONSTRAINT PKC_System PRIMARY KEY CLUSTERED (Value) WITH (DATA_COMPRESSION = PAGE)
+);
+
+CREATE TABLE [dbo].[TaskInfo] (
+ [TaskId] VARCHAR (64) NOT NULL,
+ [QueueId] VARCHAR (64) NOT NULL,
+ [Status] SMALLINT NOT NULL,
+ [TaskTypeId] SMALLINT NOT NULL,
+ [RunId] VARCHAR (50) NULL,
+ [IsCanceled] BIT NOT NULL,
+ [RetryCount] SMALLINT NOT NULL,
+ [MaxRetryCount] SMALLINT NOT NULL,
+ [HeartbeatDateTime] DATETIME2 (7) NULL,
+ [InputData] VARCHAR (MAX) NOT NULL,
+ [TaskContext] VARCHAR (MAX) NULL,
+ [Result] VARCHAR (MAX) NULL,
+ [CreateDateTime] DATETIME2 (7) CONSTRAINT DF_TaskInfo_CreateDate DEFAULT SYSUTCDATETIME() NOT NULL,
+ [StartDateTime] DATETIME2 (7) NULL,
+ [EndDateTime] DATETIME2 (7) NULL,
+ [Worker] VARCHAR (100) NULL,
+ [RestartInfo] VARCHAR (MAX) NULL,
+ [ParentTaskId] VARCHAR (64) NULL,
+ CONSTRAINT PKC_TaskInfo PRIMARY KEY CLUSTERED (TaskId) WITH (DATA_COMPRESSION = PAGE)
+) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY];
+
+
+GO
+CREATE NONCLUSTERED INDEX IX_QueueId_Status
+ ON dbo.TaskInfo(QueueId, Status);
+
+
+GO
+CREATE NONCLUSTERED INDEX IX_QueueId_ParentTaskId
+ ON dbo.TaskInfo(QueueId, ParentTaskId);
+
+CREATE TABLE dbo.TokenDateTimeCompositeSearchParam (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId1 INT NULL,
+ Code1 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ StartDateTime2 DATETIME2 (7) NOT NULL,
+ EndDateTime2 DATETIME2 (7) NOT NULL,
+ IsLongerThanADay2 BIT NOT NULL,
+ CodeOverflow1 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL
+);
+
+ALTER TABLE dbo.TokenDateTimeCompositeSearchParam
+ ADD CONSTRAINT CHK_TokenDateTimeCompositeSearchParam_CodeOverflow1 CHECK (LEN(Code1) = 256
+ OR CodeOverflow1 IS NULL);
+
+ALTER TABLE dbo.TokenDateTimeCompositeSearchParam SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_TokenDateTimeCompositeSearchParam
+ ON dbo.TokenDateTimeCompositeSearchParam(ResourceTypeId, ResourceSurrogateId, SearchParamId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Code1_StartDateTime2_EndDateTime2_INCLUDE_SystemId1_IsLongerThanADay2
+ ON dbo.TokenDateTimeCompositeSearchParam(SearchParamId, Code1, StartDateTime2, EndDateTime2)
+ INCLUDE(SystemId1, IsLongerThanADay2) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Code1_EndDateTime2_StartDateTime2_INCLUDE_SystemId1_IsLongerThanADay2
+ ON dbo.TokenDateTimeCompositeSearchParam(SearchParamId, Code1, EndDateTime2, StartDateTime2)
+ INCLUDE(SystemId1, IsLongerThanADay2) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Code1_StartDateTime2_EndDateTime2_INCLUDE_SystemId1_WHERE_IsLongerThanADay2_1
+ ON dbo.TokenDateTimeCompositeSearchParam(SearchParamId, Code1, StartDateTime2, EndDateTime2)
+ INCLUDE(SystemId1) WHERE IsLongerThanADay2 = 1 WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Code1_EndDateTime2_StartDateTime2_INCLUDE_SystemId1_WHERE_IsLongerThanADay2_1
+ ON dbo.TokenDateTimeCompositeSearchParam(SearchParamId, Code1, EndDateTime2, StartDateTime2)
+ INCLUDE(SystemId1) WHERE IsLongerThanADay2 = 1 WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.TokenNumberNumberCompositeSearchParam (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId1 INT NULL,
+ Code1 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ SingleValue2 DECIMAL (36, 18) NULL,
+ LowValue2 DECIMAL (36, 18) NULL,
+ HighValue2 DECIMAL (36, 18) NULL,
+ SingleValue3 DECIMAL (36, 18) NULL,
+ LowValue3 DECIMAL (36, 18) NULL,
+ HighValue3 DECIMAL (36, 18) NULL,
+ HasRange BIT NOT NULL,
+ CodeOverflow1 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL
+);
+
+ALTER TABLE dbo.TokenNumberNumberCompositeSearchParam
+ ADD CONSTRAINT CHK_TokenNumberNumberCompositeSearchParam_CodeOverflow1 CHECK (LEN(Code1) = 256
+ OR CodeOverflow1 IS NULL);
+
+ALTER TABLE dbo.TokenNumberNumberCompositeSearchParam SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_TokenNumberNumberCompositeSearchParam
+ ON dbo.TokenNumberNumberCompositeSearchParam(ResourceTypeId, ResourceSurrogateId, SearchParamId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Code1_SingleValue2_SingleValue3_INCLUDE_SystemId1_WHERE_HasRange_0
+ ON dbo.TokenNumberNumberCompositeSearchParam(SearchParamId, Code1, SingleValue2, SingleValue3)
+ INCLUDE(SystemId1) WHERE HasRange = 0 WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Code1_LowValue2_HighValue2_LowValue3_HighValue3_INCLUDE_SystemId1_WHERE_HasRange_1
+ ON dbo.TokenNumberNumberCompositeSearchParam(SearchParamId, Code1, LowValue2, HighValue2, LowValue3, HighValue3)
+ INCLUDE(SystemId1) WHERE HasRange = 1 WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.TokenQuantityCompositeSearchParam (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId1 INT NULL,
+ Code1 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ SystemId2 INT NULL,
+ QuantityCodeId2 INT NULL,
+ SingleValue2 DECIMAL (36, 18) NULL,
+ LowValue2 DECIMAL (36, 18) NULL,
+ HighValue2 DECIMAL (36, 18) NULL,
+ CodeOverflow1 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL
+);
+
+ALTER TABLE dbo.TokenQuantityCompositeSearchParam
+ ADD CONSTRAINT CHK_TokenQuantityCompositeSearchParam_CodeOverflow1 CHECK (LEN(Code1) = 256
+ OR CodeOverflow1 IS NULL);
+
+ALTER TABLE dbo.TokenQuantityCompositeSearchParam SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_TokenQuantityCompositeSearchParam
+ ON dbo.TokenQuantityCompositeSearchParam(ResourceTypeId, ResourceSurrogateId, SearchParamId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Code1_SingleValue2_INCLUDE_QuantityCodeId2_SystemId1_SystemId2_WHERE_SingleValue2_NOT_NULL
+ ON dbo.TokenQuantityCompositeSearchParam(SearchParamId, Code1, SingleValue2)
+ INCLUDE(QuantityCodeId2, SystemId1, SystemId2) WHERE SingleValue2 IS NOT NULL WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Code1_LowValue2_HighValue2_INCLUDE_QuantityCodeId2_SystemId1_SystemId2_WHERE_LowValue2_NOT_NULL
+ ON dbo.TokenQuantityCompositeSearchParam(SearchParamId, Code1, LowValue2, HighValue2)
+ INCLUDE(QuantityCodeId2, SystemId1, SystemId2) WHERE LowValue2 IS NOT NULL WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Code1_HighValue2_LowValue2_INCLUDE_QuantityCodeId2_SystemId1_SystemId2_WHERE_LowValue2_NOT_NULL
+ ON dbo.TokenQuantityCompositeSearchParam(SearchParamId, Code1, HighValue2, LowValue2)
+ INCLUDE(QuantityCodeId2, SystemId1, SystemId2) WHERE LowValue2 IS NOT NULL WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.TokenSearchParam (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId INT NULL,
+ Code VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CodeOverflow VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL
+);
+
+ALTER TABLE dbo.TokenSearchParam
+ ADD CONSTRAINT CHK_TokenSearchParam_CodeOverflow CHECK (LEN(Code) = 256
+ OR CodeOverflow IS NULL);
+
+ALTER TABLE dbo.TokenSearchParam SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_TokenSearchParam
+ ON dbo.TokenSearchParam(ResourceTypeId, ResourceSurrogateId, SearchParamId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Code_INCLUDE_SystemId
+ ON dbo.TokenSearchParam(SearchParamId, Code)
+ INCLUDE(SystemId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.TokenStringCompositeSearchParam (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId1 INT NULL,
+ Code1 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ Text2 NVARCHAR (256) COLLATE Latin1_General_CI_AI NOT NULL,
+ TextOverflow2 NVARCHAR (MAX) COLLATE Latin1_General_CI_AI NULL,
+ CodeOverflow1 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL
+);
+
+ALTER TABLE dbo.TokenStringCompositeSearchParam
+ ADD CONSTRAINT CHK_TokenStringCompositeSearchParam_CodeOverflow1 CHECK (LEN(Code1) = 256
+ OR CodeOverflow1 IS NULL);
+
+ALTER TABLE dbo.TokenStringCompositeSearchParam SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_TokenStringCompositeSearchParam
+ ON dbo.TokenStringCompositeSearchParam(ResourceSurrogateId, SearchParamId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Code1_Text2_INCLUDE_SystemId1_TextOverflow2
+ ON dbo.TokenStringCompositeSearchParam(SearchParamId, Code1, Text2)
+ INCLUDE(SystemId1, TextOverflow2) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Code1_Text2_INCLUDE_SystemId1_WHERE_TextOverflow2_NOT_NULL
+ ON dbo.TokenStringCompositeSearchParam(SearchParamId, Code1, Text2)
+ INCLUDE(SystemId1) WHERE TextOverflow2 IS NOT NULL WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.TokenText (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ Text NVARCHAR (400) COLLATE Latin1_General_CI_AI NOT NULL,
+ IsHistory BIT NOT NULL
+);
+
+ALTER TABLE dbo.TokenText
+ ADD CONSTRAINT DF_TokenText_IsHistory DEFAULT 0 FOR IsHistory;
+
+ALTER TABLE dbo.TokenText SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_TokenText
+ ON dbo.TokenText(ResourceTypeId, ResourceSurrogateId, SearchParamId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE NONCLUSTERED INDEX IX_TokenText_SearchParamId_Text
+ ON dbo.TokenText(ResourceTypeId, SearchParamId, Text, ResourceSurrogateId) WHERE IsHistory = 0 WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.TokenTokenCompositeSearchParam (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ SystemId1 INT NULL,
+ Code1 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ SystemId2 INT NULL,
+ Code2 VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ CodeOverflow1 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL,
+ CodeOverflow2 VARCHAR (MAX) COLLATE Latin1_General_100_CS_AS NULL
+);
+
+ALTER TABLE dbo.TokenTokenCompositeSearchParam
+ ADD CONSTRAINT CHK_TokenTokenCompositeSearchParam_CodeOverflow1 CHECK (LEN(Code1) = 256
+ OR CodeOverflow1 IS NULL);
+
+ALTER TABLE dbo.TokenTokenCompositeSearchParam
+ ADD CONSTRAINT CHK_TokenTokenCompositeSearchParam_CodeOverflow2 CHECK (LEN(Code2) = 256
+ OR CodeOverflow2 IS NULL);
+
+ALTER TABLE dbo.TokenTokenCompositeSearchParam SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_TokenTokenCompositeSearchParam
+ ON dbo.TokenTokenCompositeSearchParam(ResourceSurrogateId, SearchParamId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Code1_Code2_INCLUDE_SystemId1_SystemId2
+ ON dbo.TokenTokenCompositeSearchParam(SearchParamId, Code1, Code2)
+ INCLUDE(SystemId1, SystemId2) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.Transactions (
+ SurrogateIdRangeFirstValue BIGINT NOT NULL,
+ SurrogateIdRangeLastValue BIGINT NOT NULL,
+ Definition VARCHAR (2000) NULL,
+ IsCompleted BIT CONSTRAINT DF_Transactions_IsCompleted DEFAULT 0 NOT NULL,
+ IsSuccess BIT CONSTRAINT DF_Transactions_IsSuccess DEFAULT 0 NOT NULL,
+ IsVisible BIT CONSTRAINT DF_Transactions_IsVisible DEFAULT 0 NOT NULL,
+ IsHistoryMoved BIT CONSTRAINT DF_Transactions_IsHistoryMoved DEFAULT 0 NOT NULL,
+ CreateDate DATETIME CONSTRAINT DF_Transactions_CreateDate DEFAULT getUTCdate() NOT NULL,
+ EndDate DATETIME NULL,
+ VisibleDate DATETIME NULL,
+ HistoryMovedDate DATETIME NULL,
+ HeartbeatDate DATETIME CONSTRAINT DF_Transactions_HeartbeatDate DEFAULT getUTCdate() NOT NULL,
+ FailureReason VARCHAR (MAX) NULL,
+ IsControlledByClient BIT CONSTRAINT DF_Transactions_IsControlledByClient DEFAULT 1 NOT NULL,
+ InvisibleHistoryRemovedDate DATETIME NULL CONSTRAINT PKC_Transactions_SurrogateIdRangeFirstValue PRIMARY KEY CLUSTERED (SurrogateIdRangeFirstValue)
+);
+
+CREATE INDEX IX_IsVisible
+ ON dbo.Transactions(IsVisible);
+
+CREATE TABLE dbo.UriSearchParam (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ Uri VARCHAR (256) COLLATE Latin1_General_100_CS_AS NOT NULL
+);
+
+ALTER TABLE dbo.UriSearchParam SET (LOCK_ESCALATION = AUTO);
+
+CREATE CLUSTERED INDEX IXC_UriSearchParam
+ ON dbo.UriSearchParam(ResourceTypeId, ResourceSurrogateId, SearchParamId) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE INDEX IX_SearchParamId_Uri
+ ON dbo.UriSearchParam(SearchParamId, Uri) WITH (DATA_COMPRESSION = PAGE)
+ ON PartitionScheme_ResourceTypeId (ResourceTypeId);
+
+CREATE TABLE dbo.WatchdogLeases (
+ Watchdog VARCHAR (100) NOT NULL,
+ LeaseHolder VARCHAR (100) CONSTRAINT DF_WatchdogLeases_LeaseHolder DEFAULT '' NOT NULL,
+ LeaseEndTime DATETIME CONSTRAINT DF_WatchdogLeases_LeaseEndTime DEFAULT 0 NOT NULL,
+ RemainingLeaseTimeSec AS datediff(second, getUTCdate(), LeaseEndTime),
+ LeaseRequestor VARCHAR (100) CONSTRAINT DF_WatchdogLeases_LeaseRequestor DEFAULT '' NOT NULL,
+ LeaseRequestTime DATETIME CONSTRAINT DF_WatchdogLeases_LeaseRequestTime DEFAULT 0 NOT NULL CONSTRAINT PKC_WatchdogLeases_Watchdog PRIMARY KEY CLUSTERED (Watchdog)
+);
+
+COMMIT
+GO
+CREATE PROCEDURE dbo.AcquireReindexJobs
+@jobHeartbeatTimeoutThresholdInSeconds BIGINT, @maximumNumberOfConcurrentJobsAllowed INT
+AS
+SET NOCOUNT ON;
+SET XACT_ABORT ON;
+SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+BEGIN TRANSACTION;
+DECLARE @expirationDateTime AS DATETIME2 (7);
+SELECT @expirationDateTime = DATEADD(second, -@jobHeartbeatTimeoutThresholdInSeconds, SYSUTCDATETIME());
+DECLARE @numberOfRunningJobs AS INT;
+SELECT @numberOfRunningJobs = COUNT(*)
+FROM dbo.ReindexJob WITH (TABLOCKX)
+WHERE Status = 'Running'
+ AND HeartbeatDateTime > @expirationDateTime;
+DECLARE @limit AS INT = @maximumNumberOfConcurrentJobsAllowed - @numberOfRunningJobs;
+IF (@limit > 0)
+ BEGIN
+ DECLARE @availableJobs TABLE (
+ Id VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ JobVersion BINARY (8) NOT NULL);
+ INSERT INTO @availableJobs
+ SELECT TOP (@limit) Id,
+ JobVersion
+ FROM dbo.ReindexJob
+ WHERE (Status = 'Queued'
+ OR (Status = 'Running'
+ AND HeartbeatDateTime <= @expirationDateTime))
+ ORDER BY HeartbeatDateTime;
+ DECLARE @heartbeatDateTime AS DATETIME2 (7) = SYSUTCDATETIME();
+ UPDATE dbo.ReindexJob
+ SET Status = 'Running',
+ HeartbeatDateTime = @heartbeatDateTime,
+ RawJobRecord = JSON_MODIFY(RawJobRecord, '$.status', 'Running')
+ OUTPUT inserted.RawJobRecord, inserted.JobVersion
+ FROM dbo.ReindexJob AS job
+ INNER JOIN
+ @availableJobs AS availableJob
+ ON job.Id = availableJob.Id
+ AND job.JobVersion = availableJob.JobVersion;
+ END
+COMMIT TRANSACTION;
+
+GO
+CREATE PROCEDURE dbo.AcquireWatchdogLease
+@Watchdog VARCHAR (100), @Worker VARCHAR (100), @AllowRebalance BIT=1, @ForceAcquire BIT=0, @LeasePeriodSec FLOAT, @WorkerIsRunning BIT=0, @LeaseEndTime DATETIME OUTPUT, @IsAcquired BIT OUTPUT, @CurrentLeaseHolder VARCHAR (100)=NULL OUTPUT
+AS
+SET NOCOUNT ON;
+SET XACT_ABORT ON;
+DECLARE @SP AS VARCHAR (100) = 'AcquireWatchdogLease', @Mode AS VARCHAR (100), @msg AS VARCHAR (1000), @MyLeasesNumber AS INT, @OtherValidRequestsOrLeasesNumber AS INT, @MyValidRequestsOrLeasesNumber AS INT, @DesiredLeasesNumber AS INT, @NotLeasedWatchdogNumber AS INT, @WatchdogNumber AS INT, @Now AS DATETIME, @MyLastChangeTime AS DATETIME, @PreviousLeaseHolder AS VARCHAR (100), @Rows AS INT = 0, @NumberOfWorkers AS INT, @st AS DATETIME = getUTCdate(), @RowsInt AS INT, @Pattern AS VARCHAR (100);
+BEGIN TRY
+ SET @Mode = 'R=' + isnull(@Watchdog, 'NULL') + ' W=' + isnull(@Worker, 'NULL') + ' F=' + isnull(CONVERT (VARCHAR, @ForceAcquire), 'NULL') + ' LP=' + isnull(CONVERT (VARCHAR, @LeasePeriodSec), 'NULL');
+ SET @CurrentLeaseHolder = '';
+ SET @IsAcquired = 0;
+ SET @Now = getUTCdate();
+ SET @LeaseEndTime = @Now;
+ SET @Pattern = NULLIF ((SELECT Char
+ FROM dbo.Parameters
+ WHERE Id = 'WatchdogLeaseHolderIncludePatternFor' + @Watchdog), '');
+ IF @Pattern IS NULL
+ SET @Pattern = NULLIF ((SELECT Char
+ FROM dbo.Parameters
+ WHERE Id = 'WatchdogLeaseHolderIncludePattern'), '');
+ IF @Pattern IS NOT NULL
+ AND @Worker NOT LIKE @Pattern
+ BEGIN
+ SET @msg = 'Worker does not match include pattern=' + @Pattern;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @Rows, @Text = @msg;
+ SET @CurrentLeaseHolder = isnull((SELECT LeaseHolder
+ FROM dbo.WatchdogLeases
+ WHERE Watchdog = @Watchdog), '');
+ RETURN;
+ END
+ SET @Pattern = NULLIF ((SELECT Char
+ FROM dbo.Parameters
+ WHERE Id = 'WatchdogLeaseHolderExcludePatternFor' + @Watchdog), '');
+ IF @Pattern IS NULL
+ SET @Pattern = NULLIF ((SELECT Char
+ FROM dbo.Parameters
+ WHERE Id = 'WatchdogLeaseHolderExcludePattern'), '');
+ IF @Pattern IS NOT NULL
+ AND @Worker LIKE @Pattern
+ BEGIN
+ SET @msg = 'Worker matches exclude pattern=' + @Pattern;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @Rows, @Text = @msg;
+ SET @CurrentLeaseHolder = isnull((SELECT LeaseHolder
+ FROM dbo.WatchdogLeases
+ WHERE Watchdog = @Watchdog), '');
+ RETURN;
+ END
+ DECLARE @Watchdogs TABLE (
+ Watchdog VARCHAR (100) PRIMARY KEY);
+ INSERT INTO @Watchdogs
+ SELECT Watchdog
+ FROM dbo.WatchdogLeases WITH (NOLOCK)
+ WHERE RemainingLeaseTimeSec * (-1) > 10 * @LeasePeriodSec
+ OR @ForceAcquire = 1
+ AND Watchdog = @Watchdog
+ AND LeaseHolder <> @Worker;
+ IF @@rowcount > 0
+ BEGIN
+ DELETE dbo.WatchdogLeases
+ WHERE Watchdog IN (SELECT Watchdog
+ FROM @Watchdogs);
+ SET @Rows += @@rowcount;
+ IF @Rows > 0
+ BEGIN
+ SET @msg = '';
+ SELECT @msg = CONVERT (VARCHAR (1000), @msg + CASE WHEN @msg = '' THEN '' ELSE ',' END + Watchdog)
+ FROM @Watchdogs;
+ SET @msg = CONVERT (VARCHAR (1000), 'Remove old/forced leases:' + @msg);
+ EXECUTE dbo.LogEvent @Process = 'AcquireWatchdogLease', @Status = 'Info', @Mode = @Mode, @Target = 'WatchdogLeases', @Action = 'Delete', @Rows = @Rows, @Text = @msg;
+ END
+ END
+ SET @NumberOfWorkers = 1 + (SELECT count(*)
+ FROM (SELECT LeaseHolder
+ FROM dbo.WatchdogLeases WITH (NOLOCK)
+ WHERE LeaseHolder <> @Worker
+ UNION
+ SELECT LeaseRequestor
+ FROM dbo.WatchdogLeases WITH (NOLOCK)
+ WHERE LeaseRequestor <> @Worker
+ AND LeaseRequestor <> '') AS A);
+ SET @Mode = CONVERT (VARCHAR (100), @Mode + ' N=' + CONVERT (VARCHAR (10), @NumberOfWorkers));
+ IF NOT EXISTS (SELECT *
+ FROM dbo.WatchdogLeases WITH (NOLOCK)
+ WHERE Watchdog = @Watchdog)
+ INSERT INTO dbo.WatchdogLeases (Watchdog, LeaseEndTime, LeaseRequestTime)
+ SELECT @Watchdog,
+ dateadd(day, -10, @Now),
+ dateadd(day, -10, @Now)
+ WHERE NOT EXISTS (SELECT *
+ FROM dbo.WatchdogLeases WITH (TABLOCKX)
+ WHERE Watchdog = @Watchdog);
+ SET @LeaseEndTime = dateadd(second, @LeasePeriodSec, @Now);
+ SET @WatchdogNumber = (SELECT count(*)
+ FROM dbo.WatchdogLeases WITH (NOLOCK));
+ SET @NotLeasedWatchdogNumber = (SELECT count(*)
+ FROM dbo.WatchdogLeases WITH (NOLOCK)
+ WHERE LeaseHolder = ''
+ OR LeaseEndTime < @Now);
+ SET @MyLeasesNumber = (SELECT count(*)
+ FROM dbo.WatchdogLeases WITH (NOLOCK)
+ WHERE LeaseHolder = @Worker
+ AND LeaseEndTime > @Now);
+ SET @OtherValidRequestsOrLeasesNumber = (SELECT count(*)
+ FROM dbo.WatchdogLeases WITH (NOLOCK)
+ WHERE LeaseHolder <> @Worker
+ AND LeaseEndTime > @Now
+ OR LeaseRequestor <> @Worker
+ AND datediff(second, LeaseRequestTime, @Now) < @LeasePeriodSec);
+ SET @MyValidRequestsOrLeasesNumber = (SELECT count(*)
+ FROM dbo.WatchdogLeases WITH (NOLOCK)
+ WHERE LeaseHolder = @Worker
+ AND LeaseEndTime > @Now
+ OR LeaseRequestor = @Worker
+ AND datediff(second, LeaseRequestTime, @Now) < @LeasePeriodSec);
+ SET @DesiredLeasesNumber = ceiling(1.0 * @WatchdogNumber / @NumberOfWorkers);
+ IF @DesiredLeasesNumber = 0
+ SET @DesiredLeasesNumber = 1;
+ IF @DesiredLeasesNumber = 1
+ AND @OtherValidRequestsOrLeasesNumber = 1
+ AND @WatchdogNumber = 1
+ SET @DesiredLeasesNumber = 0;
+ IF @MyValidRequestsOrLeasesNumber = floor(1.0 * @WatchdogNumber / @NumberOfWorkers)
+ AND @OtherValidRequestsOrLeasesNumber + @MyValidRequestsOrLeasesNumber = @WatchdogNumber
+ SET @DesiredLeasesNumber = @DesiredLeasesNumber - 1;
+ UPDATE dbo.WatchdogLeases
+ SET LeaseHolder = @Worker,
+ LeaseEndTime = @LeaseEndTime,
+ LeaseRequestor = '',
+ @PreviousLeaseHolder = LeaseHolder
+ WHERE Watchdog = @Watchdog
+ AND NOT (LeaseRequestor <> @Worker
+ AND datediff(second, LeaseRequestTime, @Now) < @LeasePeriodSec)
+ AND (LeaseHolder = @Worker
+ AND (LeaseEndTime > @Now
+ OR @WorkerIsRunning = 1)
+ OR LeaseEndTime < @Now
+ AND (@DesiredLeasesNumber > @MyLeasesNumber
+ OR @OtherValidRequestsOrLeasesNumber < @WatchdogNumber));
+ IF @@rowcount > 0
+ BEGIN
+ SET @IsAcquired = 1;
+ SET @msg = 'Lease holder changed from [' + isnull(@PreviousLeaseHolder, '') + '] to [' + @Worker + ']';
+ IF @PreviousLeaseHolder <> @Worker
+ EXECUTE dbo.LogEvent @Process = 'AcquireWatchdogLease', @Status = 'Info', @Mode = @Mode, @Text = @msg;
+ END
+ ELSE
+ IF @AllowRebalance = 1
+ BEGIN
+ SET @CurrentLeaseHolder = (SELECT LeaseHolder
+ FROM dbo.WatchdogLeases
+ WHERE Watchdog = @Watchdog);
+ UPDATE dbo.WatchdogLeases
+ SET LeaseRequestTime = @Now
+ WHERE Watchdog = @Watchdog
+ AND LeaseRequestor = @Worker
+ AND datediff(second, LeaseRequestTime, @Now) < @LeasePeriodSec;
+ IF @DesiredLeasesNumber > @MyValidRequestsOrLeasesNumber
+ BEGIN
+ UPDATE A
+ SET LeaseRequestor = @Worker,
+ LeaseRequestTime = @Now
+ FROM dbo.WatchdogLeases AS A
+ WHERE Watchdog = @Watchdog
+ AND NOT (LeaseRequestor <> @Worker
+ AND datediff(second, LeaseRequestTime, @Now) < @LeasePeriodSec)
+ AND @NotLeasedWatchdogNumber = 0
+ AND (SELECT count(*)
+ FROM dbo.WatchdogLeases AS B
+ WHERE B.LeaseHolder = A.LeaseHolder
+ AND datediff(second, B.LeaseEndTime, @Now) < @LeasePeriodSec) > @DesiredLeasesNumber;
+ SET @RowsInt = @@rowcount;
+ SET @msg = '@DesiredLeasesNumber=[' + CONVERT (VARCHAR (10), @DesiredLeasesNumber) + '] > @MyValidRequestsOrLeasesNumber=[' + CONVERT (VARCHAR (10), @MyValidRequestsOrLeasesNumber) + ']';
+ EXECUTE dbo.LogEvent @Process = 'AcquireWatchdogLease', @Status = 'Info', @Mode = @Mode, @Rows = @RowsInt, @Text = @msg;
+ END
+ END
+ SET @Mode = CONVERT (VARCHAR (100), @Mode + ' A=' + CONVERT (VARCHAR (1), @IsAcquired));
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @Rows;
+END TRY
+BEGIN CATCH
+ IF @@trancount > 0
+ ROLLBACK;
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = 'AcquireWatchdogLease', @Status = 'Error', @Mode = @Mode;
+ THROW;
+END CATCH
+
+GO
+CREATE OR ALTER PROCEDURE dbo.AddPartitionOnResourceChanges
+@partitionBoundary DATETIME2 (7) OUTPUT
+AS
+BEGIN
+ SET XACT_ABORT ON;
+ BEGIN TRANSACTION;
+ DECLARE @rightPartitionBoundary AS DATETIME2 (7) = CAST ((SELECT TOP (1) value
+ FROM sys.partition_range_values AS prv
+ INNER JOIN
+ sys.partition_functions AS pf
+ ON pf.function_id = prv.function_id
+ WHERE pf.name = N'PartitionFunction_ResourceChangeData_Timestamp'
+ ORDER BY prv.boundary_id DESC) AS DATETIME2 (7));
+ DECLARE @timestamp AS DATETIME2 (7) = DATEADD(hour, DATEDIFF(hour, 0, sysutcdatetime()), 0);
+ IF (@rightPartitionBoundary < @timestamp)
+ BEGIN
+ SET @rightPartitionBoundary = @timestamp;
+ END
+ SET @rightPartitionBoundary = DATEADD(hour, 1, @rightPartitionBoundary);
+ ALTER PARTITION SCHEME PartitionScheme_ResourceChangeData_Timestamp NEXT USED [Primary];
+ ALTER PARTITION FUNCTION PartitionFunction_ResourceChangeData_Timestamp( )
+ SPLIT RANGE (@rightPartitionBoundary);
+ SET @partitionBoundary = @rightPartitionBoundary;
+ COMMIT TRANSACTION;
+END
+
+GO
+CREATE PROCEDURE dbo.ArchiveJobs
+@QueueType TINYINT
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'ArchiveJobs', @Mode AS VARCHAR (100) = '', @st AS DATETIME = getUTCdate(), @Rows AS INT = 0, @PartitionId AS TINYINT, @MaxPartitions AS TINYINT = 16, @LookedAtPartitions AS TINYINT = 0, @InflightRows AS INT = 0, @Lock AS VARCHAR (100) = 'DequeueJob_' + CONVERT (VARCHAR, @QueueType);
+BEGIN TRY
+ SET @PartitionId = @MaxPartitions * rand();
+ BEGIN TRANSACTION;
+ EXECUTE sp_getapplock @Lock, 'Exclusive';
+ WHILE @LookedAtPartitions <= @MaxPartitions
+ BEGIN
+ SET @InflightRows += (SELECT count(*)
+ FROM dbo.JobQueue
+ WHERE PartitionId = @PartitionId
+ AND QueueType = @QueueType
+ AND Status IN (0, 1));
+ SET @PartitionId = CASE WHEN @PartitionId = 15 THEN 0 ELSE @PartitionId + 1 END;
+ SET @LookedAtPartitions = @LookedAtPartitions + 1;
+ END
+ IF @InflightRows = 0
+ BEGIN
+ SET @LookedAtPartitions = 0;
+ WHILE @LookedAtPartitions <= @MaxPartitions
+ BEGIN
+ UPDATE dbo.JobQueue
+ SET Status = 5
+ WHERE PartitionId = @PartitionId
+ AND QueueType = @QueueType
+ AND Status IN (2, 3, 4);
+ SET @Rows += @@rowcount;
+ SET @PartitionId = CASE WHEN @PartitionId = 15 THEN 0 ELSE @PartitionId + 1 END;
+ SET @LookedAtPartitions = @LookedAtPartitions + 1;
+ END
+ END
+ COMMIT TRANSACTION;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @Rows;
+END TRY
+BEGIN CATCH
+ IF @@trancount > 0
+ ROLLBACK;
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.AssignResourceIdInts
+@Count INT, @FirstIdInt BIGINT OUTPUT
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'AssignResourceIdInts', @Mode AS VARCHAR (200) = 'Cnt=' + CONVERT (VARCHAR, @Count), @st AS DATETIME = getUTCdate(), @FirstValueVar AS SQL_VARIANT, @LastValueVar AS SQL_VARIANT, @SequenceRangeFirstValue AS INT;
+BEGIN TRY
+ SET @FirstValueVar = NULL;
+ WHILE @FirstValueVar IS NULL
+ BEGIN
+ EXECUTE sys.sp_sequence_get_range @sequence_name = 'dbo.ResourceIdIntMapSequence', @range_size = @Count, @range_first_value = @FirstValueVar OUTPUT, @range_last_value = @LastValueVar OUTPUT;
+ SET @SequenceRangeFirstValue = CONVERT (INT, @FirstValueVar);
+ IF @SequenceRangeFirstValue > CONVERT (INT, @LastValueVar)
+ SET @FirstValueVar = NULL;
+ END
+ SET @FirstIdInt = datediff_big(millisecond, '0001-01-01', sysUTCdatetime()) * 80000 + @SequenceRangeFirstValue;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ IF @@trancount > 0
+ ROLLBACK;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.CaptureResourceChanges
+@isDeleted BIT, @version INT, @resourceId VARCHAR (64), @resourceTypeId SMALLINT
+AS
+BEGIN
+ DECLARE @changeType AS SMALLINT;
+ IF (@isDeleted = 1)
+ BEGIN
+ SET @changeType = 2;
+ END
+ ELSE
+ BEGIN
+ IF (@version = 1)
+ BEGIN
+ SET @changeType = 0;
+ END
+ ELSE
+ BEGIN
+ SET @changeType = 1;
+ END
+ END
+ INSERT INTO dbo.ResourceChangeData (ResourceId, ResourceTypeId, ResourceVersion, ResourceChangeTypeId)
+ VALUES (@resourceId, @resourceTypeId, @version, @changeType);
+END
+
+GO
+CREATE PROCEDURE dbo.CaptureResourceIdsForChanges
+@Resources dbo.ResourceList READONLY, @ResourcesLake dbo.ResourceListLake READONLY
+AS
+SET NOCOUNT ON;
+INSERT INTO dbo.ResourceChangeData (ResourceId, ResourceTypeId, ResourceVersion, ResourceChangeTypeId)
+SELECT ResourceId,
+ ResourceTypeId,
+ Version,
+ CASE WHEN IsDeleted = 1 THEN 2 WHEN Version > 1 THEN 1 ELSE 0 END
+FROM (SELECT ResourceId,
+ ResourceTypeId,
+ Version,
+ IsHistory,
+ IsDeleted
+ FROM @Resources
+ UNION ALL
+ SELECT ResourceId,
+ ResourceTypeId,
+ Version,
+ IsHistory,
+ IsDeleted
+ FROM @ResourcesLake) AS A
+WHERE IsHistory = 0;
+
+GO
+CREATE PROCEDURE dbo.CheckActiveReindexJobs
+AS
+SET NOCOUNT ON;
+SELECT Id
+FROM dbo.ReindexJob
+WHERE Status = 'Running'
+ OR Status = 'Queued'
+ OR Status = 'Paused';
+
+GO
+CREATE PROCEDURE dbo.CleanupEventLog
+WITH EXECUTE AS 'dbo'
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'CleanupEventLog', @Mode AS VARCHAR (100) = '', @MaxDeleteRows AS INT, @MaxAllowedRows AS BIGINT, @RetentionPeriodSecond AS INT, @DeletedRows AS INT, @TotalDeletedRows AS INT = 0, @TotalRows AS INT, @Now AS DATETIME = getUTCdate();
+EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Start';
+BEGIN TRY
+ SET @MaxDeleteRows = (SELECT Number
+ FROM dbo.Parameters
+ WHERE Id = 'CleanupEventLog.DeleteBatchSize');
+ IF @MaxDeleteRows IS NULL
+ RAISERROR ('Cannot get Parameter.CleanupEventLog.DeleteBatchSize', 18, 127);
+ SET @MaxAllowedRows = (SELECT Number
+ FROM dbo.Parameters
+ WHERE Id = 'CleanupEventLog.AllowedRows');
+ IF @MaxAllowedRows IS NULL
+ RAISERROR ('Cannot get Parameter.CleanupEventLog.AllowedRows', 18, 127);
+ SET @RetentionPeriodSecond = (SELECT Number * 24 * 60 * 60
+ FROM dbo.Parameters
+ WHERE Id = 'CleanupEventLog.RetentionPeriodDay');
+ IF @RetentionPeriodSecond IS NULL
+ RAISERROR ('Cannot get Parameter.CleanupEventLog.RetentionPeriodDay', 18, 127);
+ SET @TotalRows = (SELECT sum(row_count)
+ FROM sys.dm_db_partition_stats
+ WHERE object_id = object_id('EventLog')
+ AND index_id IN (0, 1));
+ SET @DeletedRows = 1;
+ WHILE @DeletedRows > 0
+ AND EXISTS (SELECT *
+ FROM dbo.Parameters
+ WHERE Id = 'CleanupEventLog.IsEnabled'
+ AND Number = 1)
+ BEGIN
+ SET @DeletedRows = 0;
+ IF @TotalRows - @TotalDeletedRows > @MaxAllowedRows
+ BEGIN
+ DELETE TOP (@MaxDeleteRows)
+ dbo.EventLog WITH (PAGLOCK)
+ WHERE EventDate <= dateadd(second, -@RetentionPeriodSecond, @Now);
+ SET @DeletedRows = @@rowcount;
+ SET @TotalDeletedRows += @DeletedRows;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = 'EventLog', @Action = 'Delete', @Rows = @DeletedRows, @Text = @TotalDeletedRows;
+ END
+ END
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @Now;
+END TRY
+BEGIN CATCH
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.CleanupResourceIdIntMap
+@ResetAfter BIT=0
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'CleanupResourceIdIntMap', @Mode AS VARCHAR (100) = 'R=' + isnull(CONVERT (VARCHAR, @ResetAfter), 'NULL'), @st AS DATETIME = getUTCdate(), @Id AS VARCHAR (100) = 'CleanupResourceIdIntMap.LastProcessed.TypeId.ResourceIdInt', @ResourceTypeId AS SMALLINT, @ResourceIdInt AS BIGINT, @RowsToProcess AS INT, @ProcessedRows AS INT = 0, @DeletedRows AS INT = 0, @ReportDate AS DATETIME = getUTCdate();
+DECLARE @LastProcessed AS VARCHAR (100) = isnull((SELECT Char
+ FROM dbo.Parameters
+ WHERE Id = @Id), '0.0');
+BEGIN TRY
+ INSERT INTO dbo.Parameters (Id, Char)
+ SELECT @SP,
+ 'LogEvent';
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Start';
+ INSERT INTO dbo.Parameters (Id, Char)
+ SELECT @Id,
+ '0.0';
+ DECLARE @Types TABLE (
+ ResourceTypeId SMALLINT PRIMARY KEY,
+ Name VARCHAR (100));
+ DECLARE @ResourceIdInts TABLE (
+ ResourceIdInt BIGINT PRIMARY KEY);
+ INSERT INTO @Types
+ EXECUTE dbo.GetUsedResourceTypes ;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = '@Types', @Action = 'Insert', @Rows = @@rowcount;
+ SET @ResourceTypeId = substring(@LastProcessed, 1, charindex('.', @LastProcessed) - 1);
+ SET @ResourceIdInt = substring(@LastProcessed, charindex('.', @LastProcessed) + 1, 255);
+ DELETE @Types
+ WHERE ResourceTypeId < @ResourceTypeId;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = '@Types', @Action = 'Delete', @Rows = @@rowcount;
+ WHILE EXISTS (SELECT *
+ FROM @Types)
+ BEGIN
+ SET @ResourceTypeId = (SELECT TOP 1 ResourceTypeId
+ FROM @Types
+ ORDER BY ResourceTypeId);
+ SET @ProcessedRows = 0;
+ SET @DeletedRows = 0;
+ SET @RowsToProcess = 1;
+ WHILE @RowsToProcess > 0
+ BEGIN
+ DELETE @ResourceIdInts;
+ INSERT INTO @ResourceIdInts
+ SELECT TOP 100000 ResourceIdInt
+ FROM dbo.ResourceIdIntMap
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceIdInt > @ResourceIdInt
+ ORDER BY ResourceIdInt;
+ SET @RowsToProcess = @@rowcount;
+ SET @ProcessedRows += @RowsToProcess;
+ IF @RowsToProcess > 0
+ SET @ResourceIdInt = (SELECT max(ResourceIdInt)
+ FROM @ResourceIdInts);
+ SET @LastProcessed = CONVERT (VARCHAR, @ResourceTypeId) + '.' + CONVERT (VARCHAR, @ResourceIdInt);
+ DELETE A
+ FROM @ResourceIdInts AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.CurrentResources AS B
+ WHERE B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = '@ResourceIdInts.Current', @Action = 'Delete', @Rows = @@rowcount, @Text = @LastProcessed;
+ DELETE A
+ FROM @ResourceIdInts AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.HistoryResources AS B
+ WHERE B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = '@ResourceIdInts.History', @Action = 'Delete', @Rows = @@rowcount, @Text = @LastProcessed;
+ DELETE A
+ FROM @ResourceIdInts AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.ResourceReferenceSearchParams AS B
+ WHERE B.ReferenceResourceTypeId = @ResourceTypeId
+ AND B.ReferenceResourceIdInt = A.ResourceIdInt);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = '@ResourceIdInts.Reference', @Action = 'Delete', @Rows = @@rowcount, @Text = @LastProcessed;
+ IF EXISTS (SELECT *
+ FROM @ResourceIdInts)
+ BEGIN
+ DELETE A
+ FROM dbo.ResourceIdIntMap AS A
+ WHERE A.ResourceTypeId = @ResourceTypeId
+ AND EXISTS (SELECT *
+ FROM @ResourceIdInts AS B
+ WHERE B.ResourceIdInt = A.ResourceIdInt);
+ SET @DeletedRows += @@rowcount;
+ END
+ UPDATE dbo.Parameters
+ SET Char = @LastProcessed
+ WHERE Id = @Id;
+ IF datediff(second, @ReportDate, getUTCdate()) > 60
+ BEGIN
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = 'ResourceIdIntMap', @Action = 'Select', @Rows = @ProcessedRows, @Text = @LastProcessed;
+ SET @ReportDate = getUTCdate();
+ SET @ProcessedRows = 0;
+ END
+ END
+ DELETE @Types
+ WHERE ResourceTypeId = @ResourceTypeId;
+ SET @ResourceIdInt = 0;
+ END
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = 'ResourceIdIntMap', @Action = 'Delete', @Rows = @DeletedRows, @Text = @LastProcessed;
+ IF @ResetAfter = 1
+ DELETE dbo.Parameters
+ WHERE Id = @Id;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE OR ALTER PROCEDURE dbo.ConfigurePartitionOnResourceChanges
+@numberOfFuturePartitionsToAdd INT
+AS
+BEGIN
+ SET XACT_ABORT ON;
+ BEGIN TRANSACTION;
+ DECLARE @partitionBoundary AS DATETIME2 (7) = DATEADD(hour, DATEDIFF(hour, 0, sysutcdatetime()), 0);
+ DECLARE @startingRightPartitionBoundary AS DATETIME2 (7) = CAST ((SELECT TOP (1) value
+ FROM sys.partition_range_values AS prv
+ INNER JOIN
+ sys.partition_functions AS pf
+ ON pf.function_id = prv.function_id
+ WHERE pf.name = N'PartitionFunction_ResourceChangeData_Timestamp'
+ ORDER BY prv.boundary_id DESC) AS DATETIME2 (7));
+ DECLARE @numberOfPartitionsToAdd AS INT = @numberOfFuturePartitionsToAdd + 1;
+ WHILE @numberOfPartitionsToAdd > 0
+ BEGIN
+ IF (@startingRightPartitionBoundary < @partitionBoundary)
+ BEGIN
+ ALTER PARTITION SCHEME PartitionScheme_ResourceChangeData_Timestamp NEXT USED [PRIMARY];
+ ALTER PARTITION FUNCTION PartitionFunction_ResourceChangeData_Timestamp( )
+ SPLIT RANGE (@partitionBoundary);
+ END
+ SET @partitionBoundary = DATEADD(hour, 1, @partitionBoundary);
+ SET @numberOfPartitionsToAdd -= 1;
+ END
+ COMMIT TRANSACTION;
+END
+
+GO
+CREATE PROCEDURE dbo.CreateReindexJob
+@id VARCHAR (64), @status VARCHAR (10), @rawJobRecord VARCHAR (MAX)
+AS
+SET NOCOUNT ON;
+SET XACT_ABORT ON;
+BEGIN TRANSACTION;
+DECLARE @heartbeatDateTime AS DATETIME2 (7) = SYSUTCDATETIME();
+INSERT INTO dbo.ReindexJob (Id, Status, HeartbeatDateTime, RawJobRecord)
+VALUES (@id, @status, @heartbeatDateTime, @rawJobRecord);
+SELECT CAST (MIN_ACTIVE_ROWVERSION() AS INT);
+COMMIT TRANSACTION;
+
+GO
+CREATE PROCEDURE dbo.CreateResourceSearchParamStats
+@Table VARCHAR (100), @Column VARCHAR (100), @ResourceTypeId SMALLINT, @SearchParamId SMALLINT
+WITH EXECUTE AS 'dbo'
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = object_name(@@procid), @Mode AS VARCHAR (200) = 'T=' + isnull(@Table, 'NULL') + ' C=' + isnull(@Column, 'NULL') + ' RT=' + isnull(CONVERT (VARCHAR, @ResourceTypeId), 'NULL') + ' SP=' + isnull(CONVERT (VARCHAR, @SearchParamId), 'NULL'), @st AS DATETIME = getUTCdate();
+BEGIN TRY
+ IF @Table IS NULL
+ OR @Column IS NULL
+ OR @ResourceTypeId IS NULL
+ OR @SearchParamId IS NULL
+ RAISERROR ('@TableName IS NULL OR @KeyColumn IS NULL OR @ResourceTypeId IS NULL OR @SearchParamId IS NULL', 18, 127);
+ EXECUTE ('CREATE STATISTICS ST_' + @Column + '_WHERE_ResourceTypeId_' + @ResourceTypeId + '_SearchParamId_' + @SearchParamId + ' ON dbo.' + @Table + ' (' + @Column + ') WHERE ResourceTypeId = ' + @ResourceTypeId + ' AND SearchParamId = ' + @SearchParamId);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Text = 'Stats created';
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ IF error_number() = 1927
+ BEGIN
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st;
+ RETURN;
+ END
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.Defrag
+@TableName VARCHAR (100), @IndexName VARCHAR (200), @PartitionNumber INT, @IsPartitioned BIT
+WITH EXECUTE AS 'dbo'
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = object_name(@@procid), @Mode AS VARCHAR (200) = @TableName + '.' + @IndexName + '.' + CONVERT (VARCHAR, @PartitionNumber) + '.' + CONVERT (VARCHAR, @IsPartitioned), @st AS DATETIME = getUTCdate(), @SQL AS VARCHAR (3500), @msg AS VARCHAR (1000), @SizeBefore AS FLOAT, @SizeAfter AS FLOAT, @IndexId AS INT, @Operation AS VARCHAR (50) = CASE WHEN EXISTS (SELECT *
+ FROM dbo.Parameters
+ WHERE Id = 'Defrag.IndexRebuild.IsEnabled'
+ AND Number = 1) THEN 'REBUILD' ELSE 'REORGANIZE' END;
+SET @Mode = @Mode + ' ' + @Operation;
+BEGIN TRY
+ SET @IndexId = (SELECT index_id
+ FROM sys.indexes
+ WHERE object_id = object_id(@TableName)
+ AND name = @IndexName);
+ SET @Sql = 'ALTER INDEX ' + quotename(@IndexName) + ' ON dbo.' + quotename(@TableName) + ' ' + @Operation + CASE WHEN @IsPartitioned = 1 THEN ' PARTITION = ' + CONVERT (VARCHAR, @PartitionNumber) ELSE '' END + CASE WHEN @Operation = 'REBUILD' THEN ' WITH (ONLINE = ON' + CASE WHEN EXISTS (SELECT *
+ FROM sys.partitions
+ WHERE object_id = object_id(@TableName)
+ AND index_id = @IndexId
+ AND data_compression_desc = 'PAGE') THEN ', DATA_COMPRESSION = PAGE' ELSE '' END + ')' ELSE '' END;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Start', @Text = @Sql;
+ SET @SizeBefore = (SELECT sum(reserved_page_count)
+ FROM sys.dm_db_partition_stats
+ WHERE object_id = object_id(@TableName)
+ AND index_id = @IndexId
+ AND partition_number = @PartitionNumber) * 8.0 / 1024 / 1024;
+ SET @msg = 'Size[GB] before=' + CONVERT (VARCHAR, @SizeBefore);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Text = @msg;
+ BEGIN TRY
+ EXECUTE (@Sql);
+ SET @SizeAfter = (SELECT sum(reserved_page_count)
+ FROM sys.dm_db_partition_stats
+ WHERE object_id = object_id(@TableName)
+ AND index_id = @IndexId
+ AND partition_number = @PartitionNumber) * 8.0 / 1024 / 1024;
+ SET @msg = 'Size[GB] before=' + CONVERT (VARCHAR, @SizeBefore) + ', after=' + CONVERT (VARCHAR, @SizeAfter) + ', reduced by=' + CONVERT (VARCHAR, @SizeBefore - @SizeAfter);
+ EXECUTE dbo.LogEvent @Process = @SP, @Status = 'End', @Mode = @Mode, @Action = @Operation, @Start = @st, @Text = @msg;
+ END TRY
+ BEGIN CATCH
+ EXECUTE dbo.LogEvent @Process = @SP, @Status = 'Error', @Mode = @Mode, @Action = @Operation, @Start = @st;
+ THROW;
+ END CATCH
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.DefragChangeDatabaseSettings
+@IsOn BIT
+WITH EXECUTE AS 'dbo'
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'DefragChangeDatabaseSettings', @Mode AS VARCHAR (200) = 'On=' + CONVERT (VARCHAR, @IsOn), @st AS DATETIME = getUTCdate(), @SQL AS VARCHAR (3500);
+BEGIN TRY
+ EXECUTE dbo.LogEvent @Process = @SP, @Status = 'Start', @Mode = @Mode;
+ SET @SQL = 'ALTER DATABASE CURRENT SET AUTO_UPDATE_STATISTICS ' + CASE WHEN @IsOn = 1 THEN 'ON' ELSE 'OFF' END;
+ EXECUTE (@SQL);
+ EXECUTE dbo.LogEvent @Process = @SP, @Status = 'Run', @Mode = @Mode, @Text = @SQL;
+ SET @SQL = 'ALTER DATABASE CURRENT SET AUTO_CREATE_STATISTICS ' + CASE WHEN @IsOn = 1 THEN 'ON' ELSE 'OFF' END;
+ EXECUTE (@SQL);
+ EXECUTE dbo.LogEvent @Process = @SP, @Status = 'End', @Mode = @Mode, @Start = @st, @Text = @SQL;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.DefragGetFragmentation
+@TableName VARCHAR (200), @IndexName VARCHAR (200)=NULL, @PartitionNumber INT=NULL
+WITH EXECUTE AS 'dbo'
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = object_name(@@procid), @st AS DATETIME = getUTCdate(), @msg AS VARCHAR (1000), @Rows AS INT, @MinFragPct AS INT = isnull((SELECT Number
+ FROM dbo.Parameters
+ WHERE Id = 'Defrag.MinFragPct'), 10), @MinSizeGB AS FLOAT = isnull((SELECT Number
+ FROM dbo.Parameters
+ WHERE Id = 'Defrag.MinSizeGB'), 0.1), @PreviousGroupId AS BIGINT, @IndexId AS INT;
+DECLARE @Mode AS VARCHAR (200) = 'T=' + @TableName + ' I=' + isnull(@IndexName, 'NULL') + ' P=' + isnull(CONVERT (VARCHAR, @PartitionNumber), 'NULL') + ' MF=' + CONVERT (VARCHAR, @MinFragPct) + ' MS=' + CONVERT (VARCHAR, @MinSizeGB);
+BEGIN TRY
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Start';
+ IF object_id(@TableName) IS NULL
+ RAISERROR ('Table does not exist', 18, 127);
+ SET @IndexId = (SELECT index_id
+ FROM sys.indexes
+ WHERE object_id = object_id(@TableName)
+ AND name = @IndexName);
+ IF @IndexName IS NOT NULL
+ AND @IndexId IS NULL
+ RAISERROR ('Index does not exist', 18, 127);
+ SET @PreviousGroupId = (SELECT TOP 1 GroupId
+ FROM dbo.JobQueue
+ WHERE QueueType = 3
+ AND Status = 5
+ AND Definition = @TableName
+ ORDER BY GroupId DESC);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = '@PreviousGroupId', @Text = @PreviousGroupId;
+ SELECT TableName,
+ IndexName,
+ partition_number,
+ frag_in_percent
+ FROM (SELECT @TableName AS TableName,
+ I.name AS IndexName,
+ partition_number,
+ avg_fragmentation_in_percent AS frag_in_percent,
+ isnull(CONVERT (FLOAT, Result), 0) AS prev_frag_in_percent
+ FROM (SELECT object_id,
+ index_id,
+ partition_number,
+ avg_fragmentation_in_percent
+ FROM sys.dm_db_index_physical_stats(db_id(), object_id(@TableName), @IndexId, @PartitionNumber, 'LIMITED') AS A
+ WHERE index_id > 0
+ AND (@PartitionNumber IS NOT NULL
+ OR avg_fragmentation_in_percent >= @MinFragPct
+ AND A.page_count > @MinSizeGB * 1024 * 1024 / 8)) AS A
+ INNER JOIN
+ sys.indexes AS I
+ ON I.object_id = A.object_id
+ AND I.index_id = A.index_id
+ LEFT OUTER JOIN
+ dbo.JobQueue
+ ON QueueType = 3
+ AND Status = 5
+ AND GroupId = @PreviousGroupId
+ AND Definition = I.name + ';' + CONVERT (VARCHAR, partition_number)) AS A
+ WHERE @PartitionNumber IS NOT NULL
+ OR frag_in_percent >= prev_frag_in_percent + @MinFragPct;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @@rowcount;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.DeleteHistory
+@DeleteResources BIT=0, @Reset BIT=0, @DisableLogEvent BIT=0
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'DeleteHistory', @Mode AS VARCHAR (100) = 'D=' + isnull(CONVERT (VARCHAR, @DeleteResources), 'NULL') + ' R=' + isnull(CONVERT (VARCHAR, @Reset), 'NULL'), @st AS DATETIME = getUTCdate(), @Id AS VARCHAR (100) = 'DeleteHistory.LastProcessed.TypeId.SurrogateId', @ResourceTypeId AS SMALLINT, @SurrogateId AS BIGINT, @RowsToProcess AS INT, @ProcessedResources AS INT = 0, @DeletedResources AS INT = 0, @DeletedSearchParams AS INT = 0, @ReportDate AS DATETIME = getUTCdate();
+BEGIN TRY
+ IF @DisableLogEvent = 0
+ INSERT INTO dbo.Parameters (Id, Char)
+ SELECT @SP,
+ 'LogEvent';
+ ELSE
+ DELETE dbo.Parameters
+ WHERE Id = @SP;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Start';
+ INSERT INTO dbo.Parameters (Id, Char)
+ SELECT @Id,
+ '0.0'
+ WHERE NOT EXISTS (SELECT *
+ FROM dbo.Parameters
+ WHERE Id = @Id);
+ DECLARE @LastProcessed AS VARCHAR (100) = CASE WHEN @Reset = 0 THEN (SELECT Char
+ FROM dbo.Parameters
+ WHERE Id = @Id) ELSE '0.0' END;
+ DECLARE @Types TABLE (
+ ResourceTypeId SMALLINT PRIMARY KEY,
+ Name VARCHAR (100));
+ DECLARE @SurrogateIds TABLE (
+ ResourceSurrogateId BIGINT PRIMARY KEY,
+ IsHistory BIT );
+ INSERT INTO @Types
+ EXECUTE dbo.GetUsedResourceTypes ;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = '@Types', @Action = 'Insert', @Rows = @@rowcount;
+ SET @ResourceTypeId = substring(@LastProcessed, 1, charindex('.', @LastProcessed) - 1);
+ SET @SurrogateId = substring(@LastProcessed, charindex('.', @LastProcessed) + 1, 255);
+ DELETE @Types
+ WHERE ResourceTypeId < @ResourceTypeId;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = '@Types', @Action = 'Delete', @Rows = @@rowcount;
+ WHILE EXISTS (SELECT *
+ FROM @Types)
+ BEGIN
+ SET @ResourceTypeId = (SELECT TOP 1 ResourceTypeId
+ FROM @Types
+ ORDER BY ResourceTypeId);
+ SET @ProcessedResources = 0;
+ SET @DeletedResources = 0;
+ SET @DeletedSearchParams = 0;
+ SET @RowsToProcess = 1;
+ WHILE @RowsToProcess > 0
+ BEGIN
+ DELETE @SurrogateIds;
+ INSERT INTO @SurrogateIds
+ SELECT TOP 10000 ResourceSurrogateId,
+ IsHistory
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId > @SurrogateId
+ ORDER BY ResourceSurrogateId;
+ SET @RowsToProcess = @@rowcount;
+ SET @ProcessedResources += @RowsToProcess;
+ IF @RowsToProcess > 0
+ SET @SurrogateId = (SELECT max(ResourceSurrogateId)
+ FROM @SurrogateIds);
+ SET @LastProcessed = CONVERT (VARCHAR, @ResourceTypeId) + '.' + CONVERT (VARCHAR, @SurrogateId);
+ DELETE @SurrogateIds
+ WHERE IsHistory = 0;
+ IF EXISTS (SELECT *
+ FROM @SurrogateIds)
+ BEGIN
+ DELETE dbo.ResourceWriteClaim
+ WHERE ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.CompartmentAssignment
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.ReferenceSearchParam
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.TokenSearchParam
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.TokenText
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.StringSearchParam
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.UriSearchParam
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.NumberSearchParam
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.QuantitySearchParam
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.DateTimeSearchParam
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.ReferenceTokenCompositeSearchParam
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.TokenTokenCompositeSearchParam
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.TokenDateTimeCompositeSearchParam
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.TokenQuantityCompositeSearchParam
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.TokenStringCompositeSearchParam
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ DELETE dbo.TokenNumberNumberCompositeSearchParam
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedSearchParams += @@rowcount;
+ IF @DeleteResources = 1
+ BEGIN
+ DELETE dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId IN (SELECT ResourceSurrogateId
+ FROM @SurrogateIds);
+ SET @DeletedResources += @@rowcount;
+ END
+ END
+ UPDATE dbo.Parameters
+ SET Char = @LastProcessed
+ WHERE Id = @Id;
+ IF datediff(second, @ReportDate, getUTCdate()) > 60
+ BEGIN
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = 'Resource', @Action = 'Select', @Rows = @ProcessedResources, @Text = @LastProcessed;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = '*SearchParam', @Action = 'Delete', @Rows = @DeletedSearchParams, @Text = @LastProcessed;
+ IF @DeleteResources = 1
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = 'Resource', @Action = 'Delete', @Rows = @DeletedResources, @Text = @LastProcessed;
+ SET @ReportDate = getUTCdate();
+ SET @ProcessedResources = 0;
+ SET @DeletedSearchParams = 0;
+ SET @DeletedResources = 0;
+ END
+ END
+ DELETE @Types
+ WHERE ResourceTypeId = @ResourceTypeId;
+ SET @SurrogateId = 0;
+ END
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = 'Resource', @Action = 'Select', @Rows = @ProcessedResources, @Text = @LastProcessed;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = '*SearchParam', @Action = 'Delete', @Rows = @DeletedSearchParams, @Text = @LastProcessed;
+ IF @DeleteResources = 1
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = 'Resource', @Action = 'Delete', @Rows = @DeletedResources, @Text = @LastProcessed;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.DequeueJob
+@QueueType TINYINT, @Worker VARCHAR (100), @HeartbeatTimeoutSec INT, @InputJobId BIGINT=NULL, @CheckTimeoutJobs BIT=0
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'DequeueJob', @Mode AS VARCHAR (100) = 'Q=' + isnull(CONVERT (VARCHAR, @QueueType), 'NULL') + ' H=' + isnull(CONVERT (VARCHAR, @HeartbeatTimeoutSec), 'NULL') + ' W=' + isnull(@Worker, 'NULL') + ' IJ=' + isnull(CONVERT (VARCHAR, @InputJobId), 'NULL') + ' T=' + isnull(CONVERT (VARCHAR, @CheckTimeoutJobs), 'NULL'), @Rows AS INT = 0, @st AS DATETIME = getUTCdate(), @JobId AS BIGINT, @msg AS VARCHAR (100), @Lock AS VARCHAR (100), @PartitionId AS TINYINT, @MaxPartitions AS TINYINT = 16, @LookedAtPartitions AS TINYINT = 0;
+BEGIN TRY
+ IF EXISTS (SELECT *
+ FROM dbo.Parameters
+ WHERE Id = 'DequeueJobStop'
+ AND Number = 1)
+ BEGIN
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = 0, @Text = 'Skipped';
+ RETURN;
+ END
+ IF @InputJobId IS NULL
+ SET @PartitionId = @MaxPartitions * rand();
+ ELSE
+ SET @PartitionId = @InputJobId % 16;
+ SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
+ WHILE @InputJobId IS NULL
+ AND @JobId IS NULL
+ AND @LookedAtPartitions < @MaxPartitions
+ AND @CheckTimeoutJobs = 0
+ BEGIN
+ SET @Lock = 'DequeueJob_' + CONVERT (VARCHAR, @QueueType) + '_' + CONVERT (VARCHAR, @PartitionId);
+ BEGIN TRANSACTION;
+ EXECUTE sp_getapplock @Lock, 'Exclusive';
+ UPDATE T
+ SET StartDate = getUTCdate(),
+ HeartbeatDate = getUTCdate(),
+ Worker = @Worker,
+ Status = 1,
+ Version = datediff_big(millisecond, '0001-01-01', getUTCdate()),
+ @JobId = T.JobId
+ FROM dbo.JobQueue AS T WITH (PAGLOCK)
+ INNER JOIN
+ (SELECT TOP 1 JobId
+ FROM dbo.JobQueue WITH (INDEX (IX_QueueType_PartitionId_Status_Priority))
+ WHERE QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND Status = 0
+ ORDER BY Priority, JobId) AS S
+ ON QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND T.JobId = S.JobId;
+ SET @Rows += @@rowcount;
+ COMMIT TRANSACTION;
+ IF @JobId IS NULL
+ BEGIN
+ SET @PartitionId = CASE WHEN @PartitionId = 15 THEN 0 ELSE @PartitionId + 1 END;
+ SET @LookedAtPartitions = @LookedAtPartitions + 1;
+ END
+ END
+ SET @LookedAtPartitions = 0;
+ WHILE @InputJobId IS NULL
+ AND @JobId IS NULL
+ AND @LookedAtPartitions < @MaxPartitions
+ BEGIN
+ SET @Lock = 'DequeueStoreCopyWorkUnit_' + CONVERT (VARCHAR, @PartitionId);
+ BEGIN TRANSACTION;
+ EXECUTE sp_getapplock @Lock, 'Exclusive';
+ UPDATE T
+ SET StartDate = getUTCdate(),
+ HeartbeatDate = getUTCdate(),
+ Worker = @Worker,
+ Status = CASE WHEN CancelRequested = 0 THEN 1 ELSE 4 END,
+ Version = datediff_big(millisecond, '0001-01-01', getUTCdate()),
+ @JobId = CASE WHEN CancelRequested = 0 THEN T.JobId END,
+ Info = CONVERT (VARCHAR (1000), isnull(Info, '') + ' Prev: Worker=' + Worker + ' Start=' + CONVERT (VARCHAR, StartDate, 121))
+ FROM dbo.JobQueue AS T WITH (PAGLOCK)
+ INNER JOIN
+ (SELECT TOP 1 JobId
+ FROM dbo.JobQueue WITH (INDEX (IX_QueueType_PartitionId_Status_Priority))
+ WHERE QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND Status = 1
+ AND datediff(second, HeartbeatDate, getUTCdate()) > @HeartbeatTimeoutSec
+ ORDER BY Priority, JobId) AS S
+ ON QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND T.JobId = S.JobId;
+ SET @Rows += @@rowcount;
+ COMMIT TRANSACTION;
+ IF @JobId IS NULL
+ BEGIN
+ SET @PartitionId = CASE WHEN @PartitionId = 15 THEN 0 ELSE @PartitionId + 1 END;
+ SET @LookedAtPartitions = @LookedAtPartitions + 1;
+ END
+ END
+ IF @InputJobId IS NOT NULL
+ BEGIN
+ UPDATE dbo.JobQueue WITH (PAGLOCK)
+ SET StartDate = getUTCdate(),
+ HeartbeatDate = getUTCdate(),
+ Worker = @Worker,
+ Status = 1,
+ Version = datediff_big(millisecond, '0001-01-01', getUTCdate()),
+ @JobId = JobId
+ WHERE QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND Status = 0
+ AND JobId = @InputJobId;
+ SET @Rows += @@rowcount;
+ IF @JobId IS NULL
+ BEGIN
+ UPDATE dbo.JobQueue WITH (PAGLOCK)
+ SET StartDate = getUTCdate(),
+ HeartbeatDate = getUTCdate(),
+ Worker = @Worker,
+ Status = 1,
+ Version = datediff_big(millisecond, '0001-01-01', getUTCdate()),
+ @JobId = JobId
+ WHERE QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND Status = 1
+ AND JobId = @InputJobId
+ AND datediff(second, HeartbeatDate, getUTCdate()) > @HeartbeatTimeoutSec;
+ SET @Rows += @@rowcount;
+ END
+ END
+ IF @JobId IS NOT NULL
+ EXECUTE dbo.GetJobs @QueueType = @QueueType, @JobId = @JobId;
+ SET @msg = 'J=' + isnull(CONVERT (VARCHAR, @JobId), 'NULL') + ' P=' + CONVERT (VARCHAR, @PartitionId);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @Rows, @Text = @msg;
+END TRY
+BEGIN CATCH
+ IF @@trancount > 0
+ ROLLBACK;
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.DisableIndex
+@tableName NVARCHAR (128), @indexName NVARCHAR (128)
+WITH EXECUTE AS 'dbo'
+AS
+DECLARE @errorTxt AS VARCHAR (1000), @sql AS NVARCHAR (1000), @isDisabled AS BIT;
+IF object_id(@tableName) IS NULL
+ BEGIN
+ SET @errorTxt = @tableName + ' does not exist or you don''t have permissions.';
+ RAISERROR (@errorTxt, 18, 127);
+ END
+SET @isDisabled = (SELECT is_disabled
+ FROM sys.indexes
+ WHERE object_id = object_id(@tableName)
+ AND name = @indexName);
+IF @isDisabled IS NULL
+ BEGIN
+ SET @errorTxt = @indexName + ' does not exist or you don''t have permissions.';
+ RAISERROR (@errorTxt, 18, 127);
+ END
+IF @isDisabled = 0
+ BEGIN
+ SET @sql = N'ALTER INDEX ' + QUOTENAME(@indexName) + N' on ' + @tableName + ' Disable';
+ EXECUTE sp_executesql @sql;
+ END
+
+GO
+CREATE PROCEDURE dbo.DisableIndexes
+WITH EXECUTE AS 'dbo'
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'DisableIndexes', @Mode AS VARCHAR (200) = '', @st AS DATETIME = getUTCdate(), @Tbl AS VARCHAR (100), @Ind AS VARCHAR (200), @Txt AS VARCHAR (4000);
+BEGIN TRY
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Start';
+ DECLARE @Tables TABLE (
+ Tbl VARCHAR (100) PRIMARY KEY,
+ Supported BIT );
+ INSERT INTO @Tables
+ EXECUTE dbo.GetPartitionedTables @IncludeNotDisabled = 1, @IncludeNotSupported = 0;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '@Tables', @Action = 'Insert', @Rows = @@rowcount;
+ DECLARE @Indexes TABLE (
+ Tbl VARCHAR (100),
+ Ind VARCHAR (200),
+ TblId INT ,
+ IndId INT PRIMARY KEY (Tbl, Ind));
+ INSERT INTO @Indexes
+ SELECT Tbl,
+ I.Name,
+ TblId,
+ I.index_id
+ FROM (SELECT object_id(Tbl) AS TblId,
+ Tbl
+ FROM @Tables) AS O
+ INNER JOIN
+ sys.indexes AS I
+ ON I.object_id = TblId;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '@Indexes', @Action = 'Insert', @Rows = @@rowcount;
+ INSERT INTO dbo.IndexProperties (TableName, IndexName, PropertyName, PropertyValue)
+ SELECT Tbl,
+ Ind,
+ 'DATA_COMPRESSION',
+ data_comp
+ FROM (SELECT Tbl,
+ Ind,
+ isnull((SELECT TOP 1 CASE WHEN data_compression_desc = 'PAGE' THEN 'PAGE' END
+ FROM sys.partitions
+ WHERE object_id = TblId
+ AND index_id = IndId), 'NONE') AS data_comp
+ FROM @Indexes) AS A
+ WHERE NOT EXISTS (SELECT *
+ FROM dbo.IndexProperties
+ WHERE TableName = Tbl
+ AND IndexName = Ind);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = 'IndexProperties', @Action = 'Insert', @Rows = @@rowcount;
+ DELETE @Indexes
+ WHERE Tbl IN ('Resource', 'ResourceCurrent', 'ResourceHistory')
+ OR IndId = 1;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '@Indexes', @Action = 'Delete', @Rows = @@rowcount;
+ WHILE EXISTS (SELECT *
+ FROM @Indexes)
+ BEGIN
+ SELECT TOP 1 @Tbl = Tbl,
+ @Ind = Ind
+ FROM @Indexes;
+ SET @Txt = 'ALTER INDEX ' + @Ind + ' ON dbo.' + @Tbl + ' DISABLE';
+ EXECUTE (@Txt);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @Ind, @Action = 'Disable', @Text = @Txt;
+ DELETE @Indexes
+ WHERE Tbl = @Tbl
+ AND Ind = @Ind;
+ END
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.EnqueueJobs
+@QueueType TINYINT, @Definitions StringList READONLY, @GroupId BIGINT=NULL, @ForceOneActiveJobGroup BIT=1, @IsCompleted BIT=NULL, @Status TINYINT=NULL, @Result VARCHAR (MAX)=NULL, @StartDate DATETIME=NULL, @ReturnJobs BIT=1
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'EnqueueJobs', @Mode AS VARCHAR (100) = 'Q=' + isnull(CONVERT (VARCHAR, @QueueType), 'NULL') + ' D=' + CONVERT (VARCHAR, (SELECT count(*)
+ FROM @Definitions)) + ' G=' + isnull(CONVERT (VARCHAR, @GroupId), 'NULL') + ' F=' + isnull(CONVERT (VARCHAR, @ForceOneActiveJobGroup), 'NULL') + ' S=' + isnull(CONVERT (VARCHAR, @Status), 'NULL'), @st AS DATETIME = getUTCdate(), @Lock AS VARCHAR (100) = 'EnqueueJobs_' + CONVERT (VARCHAR, @QueueType), @MaxJobId AS BIGINT, @Rows AS INT, @msg AS VARCHAR (1000), @JobIds AS BigintList, @InputRows AS INT;
+BEGIN TRY
+ DECLARE @Input TABLE (
+ DefinitionHash VARBINARY (20) PRIMARY KEY,
+ Definition VARCHAR (MAX) );
+ INSERT INTO @Input
+ SELECT hashbytes('SHA1', String) AS DefinitionHash,
+ String AS Definition
+ FROM @Definitions;
+ SET @InputRows = @@rowcount;
+ INSERT INTO @JobIds
+ SELECT JobId
+ FROM @Input AS A
+ INNER JOIN
+ dbo.JobQueue AS B
+ ON B.QueueType = @QueueType
+ AND B.DefinitionHash = A.DefinitionHash
+ AND B.Status <> 5;
+ IF @@rowcount < @InputRows
+ BEGIN
+ BEGIN TRANSACTION;
+ EXECUTE sp_getapplock @Lock, 'Exclusive';
+ IF @ForceOneActiveJobGroup = 1
+ AND EXISTS (SELECT *
+ FROM dbo.JobQueue
+ WHERE QueueType = @QueueType
+ AND Status IN (0, 1)
+ AND (@GroupId IS NULL
+ OR GroupId <> @GroupId))
+ RAISERROR ('There are other active job groups', 18, 127);
+ SET @MaxJobId = isnull((SELECT TOP 1 JobId
+ FROM dbo.JobQueue
+ WHERE QueueType = @QueueType
+ ORDER BY JobId DESC), 0);
+ INSERT INTO dbo.JobQueue (QueueType, GroupId, JobId, Definition, DefinitionHash, Status, Result, StartDate, EndDate)
+ OUTPUT inserted.JobId INTO @JobIds
+ SELECT @QueueType,
+ isnull(@GroupId, @MaxJobId + 1) AS GroupId,
+ JobId,
+ Definition,
+ DefinitionHash,
+ isnull(@Status, 0) AS Status,
+ CASE WHEN @Status = 2 THEN @Result ELSE NULL END AS Result,
+ CASE WHEN @Status = 1 THEN getUTCdate() ELSE @StartDate END AS StartDate,
+ CASE WHEN @Status = 2 THEN getUTCdate() ELSE NULL END AS EndDate
+ FROM (SELECT @MaxJobId + row_number() OVER (ORDER BY Dummy) AS JobId,
+ *
+ FROM (SELECT *,
+ 0 AS Dummy
+ FROM @Input) AS A) AS A
+ WHERE NOT EXISTS (SELECT *
+ FROM dbo.JobQueue AS B WITH (INDEX (IX_QueueType_DefinitionHash))
+ WHERE B.QueueType = @QueueType
+ AND B.DefinitionHash = A.DefinitionHash
+ AND B.Status <> 5);
+ SET @Rows = @@rowcount;
+ COMMIT TRANSACTION;
+ END
+ IF @ReturnJobs = 1
+ EXECUTE dbo.GetJobs @QueueType = @QueueType, @JobIds = @JobIds;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @Rows;
+END TRY
+BEGIN CATCH
+ IF @@trancount > 0
+ ROLLBACK;
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.ExecuteCommandForRebuildIndexes
+@Tbl VARCHAR (100), @Ind VARCHAR (1000), @Cmd VARCHAR (MAX)
+WITH EXECUTE AS 'dbo'
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'ExecuteCommandForRebuildIndexes', @Mode AS VARCHAR (200) = 'Tbl=' + isnull(@Tbl, 'NULL'), @st AS DATETIME, @Retries AS INT = 0, @Action AS VARCHAR (100), @msg AS VARCHAR (1000);
+RetryOnTempdbError:
+BEGIN TRY
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Start', @Text = @Cmd;
+ SET @st = getUTCdate();
+ IF @Tbl IS NULL
+ RAISERROR ('@Tbl IS NULL', 18, 127);
+ IF @Cmd IS NULL
+ RAISERROR ('@Cmd IS NULL', 18, 127);
+ SET @Action = CASE WHEN @Cmd LIKE 'UPDATE STAT%' THEN 'Update statistics' WHEN @Cmd LIKE 'CREATE%INDEX%' THEN 'Create Index' WHEN @Cmd LIKE 'ALTER%INDEX%REBUILD%' THEN 'Rebuild Index' WHEN @Cmd LIKE 'ALTER%TABLE%ADD%' THEN 'Add Constraint' END;
+ IF @Action IS NULL
+ BEGIN
+ SET @msg = 'Not supported command = ' + CONVERT (VARCHAR (900), @Cmd);
+ RAISERROR (@msg, 18, 127);
+ END
+ IF @Action = 'Create Index'
+ WAITFOR DELAY '00:00:05';
+ EXECUTE (@Cmd);
+ SELECT @Ind;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Action = @Action, @Status = 'End', @Start = @st, @Text = @Cmd;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ IF error_number() = 40544
+ BEGIN
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st, @Retry = @Retries;
+ SET @Retries = @Retries + 1;
+ IF @Tbl = 'TokenText_96'
+ WAITFOR DELAY '01:00:00';
+ ELSE
+ WAITFOR DELAY '00:10:00';
+ GOTO RetryOnTempdbError;
+ END
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ THROW;
+END CATCH
+
+GO
+CREATE OR ALTER PROCEDURE dbo.FetchEventAgentCheckpoint
+@CheckpointId VARCHAR (64)
+AS
+BEGIN
+ SELECT TOP (1) CheckpointId,
+ LastProcessedDateTime,
+ LastProcessedIdentifier
+ FROM dbo.EventAgentCheckpoint
+ WHERE CheckpointId = @CheckpointId;
+END
+
+GO
+CREATE PROCEDURE dbo.FetchResourceChanges_3
+@startId BIGINT, @lastProcessedUtcDateTime DATETIME2 (7), @pageSize SMALLINT
+AS
+BEGIN
+ SET NOCOUNT ON;
+ DECLARE @precedingPartitionBoundary AS DATETIME2 (7) = (SELECT TOP (1) CAST (prv.value AS DATETIME2 (7)) AS value
+ FROM sys.partition_range_values AS prv WITH (NOLOCK)
+ INNER JOIN
+ sys.partition_functions AS pf WITH (NOLOCK)
+ ON pf.function_id = prv.function_id
+ WHERE pf.name = N'PartitionFunction_ResourceChangeData_Timestamp'
+ AND SQL_VARIANT_PROPERTY(prv.Value, 'BaseType') = 'datetime2'
+ AND CAST (prv.value AS DATETIME2 (7)) < DATEADD(HOUR, DATEDIFF(HOUR, 0, @lastProcessedUtcDateTime), 0)
+ ORDER BY prv.boundary_id DESC);
+ IF (@precedingPartitionBoundary IS NULL)
+ BEGIN
+ SET @precedingPartitionBoundary = CONVERT (DATETIME2 (7), N'1970-01-01T00:00:00.0000000');
+ END
+ DECLARE @endDateTimeToFilter AS DATETIME2 (7) = DATEADD(HOUR, 1, SYSUTCDATETIME());
+ WITH PartitionBoundaries
+ AS (SELECT CAST (prv.value AS DATETIME2 (7)) AS PartitionBoundary
+ FROM sys.partition_range_values AS prv WITH (NOLOCK)
+ INNER JOIN
+ sys.partition_functions AS pf WITH (NOLOCK)
+ ON pf.function_id = prv.function_id
+ WHERE pf.name = N'PartitionFunction_ResourceChangeData_Timestamp'
+ AND SQL_VARIANT_PROPERTY(prv.Value, 'BaseType') = 'datetime2'
+ AND CAST (prv.value AS DATETIME2 (7)) BETWEEN @precedingPartitionBoundary AND @endDateTimeToFilter)
+ SELECT TOP (@pageSize) Id,
+ Timestamp,
+ ResourceId,
+ ResourceTypeId,
+ ResourceVersion,
+ ResourceChangeTypeId
+ FROM PartitionBoundaries AS p CROSS APPLY (SELECT TOP (@pageSize) Id,
+ Timestamp,
+ ResourceId,
+ ResourceTypeId,
+ ResourceVersion,
+ ResourceChangeTypeId
+ FROM dbo.ResourceChangeData WITH (TABLOCK, HOLDLOCK)
+ WHERE Id >= @startId
+ AND $PARTITION.PartitionFunction_ResourceChangeData_Timestamp (Timestamp) = $PARTITION.PartitionFunction_ResourceChangeData_Timestamp (p.PartitionBoundary)
+ ORDER BY Id ASC) AS rcd
+ ORDER BY rcd.Id ASC;
+END
+
+GO
+CREATE PROCEDURE dbo.GetActiveJobs
+@QueueType TINYINT, @GroupId BIGINT=NULL
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'GetActiveJobs', @Mode AS VARCHAR (100) = 'Q=' + isnull(CONVERT (VARCHAR, @QueueType), 'NULL') + ' G=' + isnull(CONVERT (VARCHAR, @GroupId), 'NULL'), @st AS DATETIME = getUTCdate(), @JobIds AS BigintList, @PartitionId AS TINYINT, @MaxPartitions AS TINYINT = 16, @LookedAtPartitions AS TINYINT = 0, @Rows AS INT = 0;
+BEGIN TRY
+ SET @PartitionId = @MaxPartitions * rand();
+ WHILE @LookedAtPartitions < @MaxPartitions
+ BEGIN
+ IF @GroupId IS NULL
+ INSERT INTO @JobIds
+ SELECT JobId
+ FROM dbo.JobQueue
+ WHERE PartitionId = @PartitionId
+ AND QueueType = @QueueType
+ AND Status IN (0, 1);
+ ELSE
+ INSERT INTO @JobIds
+ SELECT JobId
+ FROM dbo.JobQueue
+ WHERE PartitionId = @PartitionId
+ AND QueueType = @QueueType
+ AND GroupId = @GroupId
+ AND Status IN (0, 1);
+ SET @Rows += @@rowcount;
+ SET @PartitionId = CASE WHEN @PartitionId = 15 THEN 0 ELSE @PartitionId + 1 END;
+ SET @LookedAtPartitions += 1;
+ END
+ IF @Rows > 0
+ EXECUTE dbo.GetJobs @QueueType = @QueueType, @JobIds = @JobIds;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @Rows;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.GetCommandsForRebuildIndexes
+@RebuildClustered BIT
+WITH EXECUTE AS 'dbo'
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'GetCommandsForRebuildIndexes', @Mode AS VARCHAR (200) = 'PS=PartitionScheme_ResourceTypeId RC=' + isnull(CONVERT (VARCHAR, @RebuildClustered), 'NULL'), @st AS DATETIME = getUTCdate(), @Tbl AS VARCHAR (100), @TblInt AS VARCHAR (100), @Ind AS VARCHAR (200), @IndId AS INT, @Supported AS BIT, @Txt AS VARCHAR (MAX), @Rows AS BIGINT, @Pages AS BIGINT, @ResourceTypeId AS SMALLINT, @IndexesCnt AS INT, @DataComp AS VARCHAR (100);
+BEGIN TRY
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Start';
+ DECLARE @Commands TABLE (
+ Tbl VARCHAR (100),
+ Ind VARCHAR (200),
+ Txt VARCHAR (MAX),
+ Pages BIGINT );
+ DECLARE @ResourceTypes TABLE (
+ ResourceTypeId SMALLINT PRIMARY KEY);
+ DECLARE @Indexes TABLE (
+ Ind VARCHAR (200) PRIMARY KEY,
+ IndId INT );
+ DECLARE @Tables TABLE (
+ name VARCHAR (100) PRIMARY KEY,
+ Supported BIT );
+ INSERT INTO @Tables
+ EXECUTE dbo.GetPartitionedTables @IncludeNotDisabled = 1, @IncludeNotSupported = 1;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '@Tables', @Action = 'Insert', @Rows = @@rowcount;
+ WHILE EXISTS (SELECT *
+ FROM @Tables)
+ BEGIN
+ SELECT TOP 1 @Tbl = name,
+ @Supported = Supported
+ FROM @Tables
+ ORDER BY name;
+ IF @Supported = 0
+ BEGIN
+ INSERT INTO @Commands
+ SELECT @Tbl,
+ name,
+ 'ALTER INDEX ' + name + ' ON dbo.' + @Tbl + ' REBUILD' + CASE WHEN (SELECT PropertyValue
+ FROM dbo.IndexProperties
+ WHERE TableName = @Tbl
+ AND IndexName = name) = 'PAGE' THEN ' PARTITION = ALL WITH (DATA_COMPRESSION = PAGE)' ELSE '' END,
+ CONVERT (BIGINT, 9e18)
+ FROM sys.indexes
+ WHERE object_id = object_id(@Tbl)
+ AND (is_disabled = 1
+ AND index_id > 1
+ AND @RebuildClustered = 0
+ OR index_id = 1
+ AND @RebuildClustered = 1);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '@Commands', @Action = 'Insert', @Rows = @@rowcount, @Text = 'Not supported tables with disabled indexes';
+ END
+ ELSE
+ BEGIN
+ DELETE @ResourceTypes;
+ INSERT INTO @ResourceTypes
+ SELECT CONVERT (SMALLINT, substring(name, charindex('_', name) + 1, 6)) AS ResourceTypeId
+ FROM sys.sysobjects
+ WHERE name LIKE @Tbl + '[_]%';
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '@ResourceTypes', @Action = 'Insert', @Rows = @@rowcount;
+ WHILE EXISTS (SELECT *
+ FROM @ResourceTypes)
+ BEGIN
+ SET @ResourceTypeId = (SELECT TOP 1 ResourceTypeId
+ FROM @ResourceTypes
+ ORDER BY ResourceTypeId);
+ SET @TblInt = @Tbl + '_' + CONVERT (VARCHAR, @ResourceTypeId);
+ SET @Pages = (SELECT dpages
+ FROM sysindexes
+ WHERE id = object_id(@TblInt)
+ AND indid IN (0, 1));
+ DELETE @Indexes;
+ INSERT INTO @Indexes
+ SELECT name,
+ index_id
+ FROM sys.indexes
+ WHERE object_id = object_id(@Tbl)
+ AND (index_id > 1
+ AND @RebuildClustered = 0
+ OR index_id = 1
+ AND @RebuildClustered = 1);
+ SET @IndexesCnt = 0;
+ WHILE EXISTS (SELECT *
+ FROM @Indexes)
+ BEGIN
+ SELECT TOP 1 @Ind = Ind,
+ @IndId = IndId
+ FROM @Indexes
+ ORDER BY Ind;
+ IF @IndId = 1
+ BEGIN
+ SET @Txt = 'ALTER INDEX ' + @Ind + ' ON dbo.' + @TblInt + ' REBUILD' + CASE WHEN (SELECT PropertyValue
+ FROM dbo.IndexProperties
+ WHERE TableName = @Tbl
+ AND IndexName = @Ind) = 'PAGE' THEN ' PARTITION = ALL WITH (DATA_COMPRESSION = PAGE)' ELSE '' END;
+ INSERT INTO @Commands
+ SELECT @TblInt,
+ @Ind,
+ @Txt,
+ @Pages;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @TblInt, @Action = 'Add command', @Rows = @@rowcount, @Text = @Txt;
+ END
+ ELSE
+ IF NOT EXISTS (SELECT *
+ FROM sys.indexes
+ WHERE object_id = object_id(@TblInt)
+ AND name = @Ind)
+ BEGIN
+ EXECUTE dbo.GetIndexCommands @Tbl = @Tbl, @Ind = @Ind, @AddPartClause = 0, @IncludeClustered = 0, @Txt = @Txt OUTPUT;
+ SET @Txt = replace(@Txt, '[' + @Tbl + ']', @TblInt);
+ IF @Txt IS NOT NULL
+ BEGIN
+ SET @IndexesCnt = @IndexesCnt + 1;
+ INSERT INTO @Commands
+ SELECT @TblInt,
+ @Ind,
+ @Txt,
+ @Pages;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @TblInt, @Action = 'Add command', @Rows = @@rowcount, @Text = @Txt;
+ END
+ END
+ DELETE @Indexes
+ WHERE Ind = @Ind;
+ END
+ IF @IndexesCnt > 1
+ BEGIN
+ INSERT INTO @Commands
+ SELECT @TblInt,
+ 'UPDATE STAT',
+ 'UPDATE STATISTICS dbo.' + @TblInt,
+ @Pages;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @TblInt, @Action = 'Add command', @Rows = @@rowcount, @Text = 'Add stats update';
+ END
+ DELETE @ResourceTypes
+ WHERE ResourceTypeId = @ResourceTypeId;
+ END
+ END
+ DELETE @Tables
+ WHERE name = @Tbl;
+ END
+ SELECT Tbl,
+ Ind,
+ Txt
+ FROM @Commands
+ ORDER BY Pages DESC, Tbl, CASE WHEN Txt LIKE 'UPDATE STAT%' THEN 0 ELSE 1 END;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '@Commands', @Action = 'Select', @Rows = @@rowcount;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.GetIndexCommands
+@Tbl VARCHAR (100), @Ind VARCHAR (200), @AddPartClause BIT, @IncludeClustered BIT, @Txt VARCHAR (MAX)=NULL OUTPUT
+WITH EXECUTE AS 'dbo'
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'GetIndexCommands', @Mode AS VARCHAR (200) = 'Tbl=' + isnull(@Tbl, 'NULL') + ' Ind=' + isnull(@Ind, 'NULL'), @st AS DATETIME = getUTCdate();
+DECLARE @Indexes TABLE (
+ Ind VARCHAR (200) PRIMARY KEY,
+ Txt VARCHAR (MAX));
+BEGIN TRY
+ IF @Tbl IS NULL
+ RAISERROR ('@Tbl IS NULL', 18, 127);
+ INSERT INTO @Indexes
+ SELECT Ind,
+ CASE WHEN is_primary_key = 1 THEN 'ALTER TABLE dbo.[' + Tbl + '] ADD PRIMARY KEY ' + CASE WHEN type = 1 THEN ' CLUSTERED' ELSE '' END ELSE 'CREATE' + CASE WHEN is_unique = 1 THEN ' UNIQUE' ELSE '' END + CASE WHEN type = 1 THEN ' CLUSTERED' ELSE '' END + ' INDEX ' + Ind + ' ON dbo.[' + Tbl + ']' END + ' (' + KeyCols + ')' + IncClause + CASE WHEN filter_def IS NOT NULL THEN ' WHERE ' + filter_def ELSE '' END + CASE WHEN data_comp IS NOT NULL THEN ' WITH (DATA_COMPRESSION = ' + data_comp + ')' ELSE '' END + CASE WHEN @AddPartClause = 1 THEN PartClause ELSE '' END
+ FROM (SELECT O.Name AS Tbl,
+ I.Name AS Ind,
+ isnull((SELECT TOP 1 CASE WHEN data_compression_desc = 'PAGE' THEN 'PAGE' END
+ FROM sys.partitions AS P
+ WHERE P.object_id = I.object_id
+ AND I.index_id = P.index_id), (SELECT NULLIF (PropertyValue, 'NONE')
+ FROM dbo.IndexProperties
+ WHERE TableName = O.Name
+ AND IndexName = I.Name
+ AND PropertyName = 'DATA_COMPRESSION')) AS data_comp,
+ replace(replace(replace(replace(I.filter_definition, '[', ''), ']', ''), '(', ''), ')', '') AS filter_def,
+ I.is_unique,
+ I.is_primary_key,
+ I.type,
+ KeyCols,
+ CASE WHEN IncCols IS NOT NULL THEN ' INCLUDE (' + IncCols + ')' ELSE '' END AS IncClause,
+ CASE WHEN EXISTS (SELECT *
+ FROM sys.partition_schemes AS S
+ WHERE S.data_space_id = I.data_space_id
+ AND name = 'PartitionScheme_ResourceTypeId') THEN ' ON PartitionScheme_ResourceTypeId (ResourceTypeId)' ELSE '' END AS PartClause
+ FROM sys.indexes AS I
+ INNER JOIN
+ sys.objects AS O
+ ON O.object_id = I.object_id CROSS APPLY (SELECT string_agg(CASE WHEN IC.key_ordinal > 0
+ AND IC.is_included_column = 0 THEN C.name END, ',') WITHIN GROUP (ORDER BY key_ordinal) AS KeyCols,
+ string_agg(CASE WHEN IC.is_included_column = 1 THEN C.name END, ',') WITHIN GROUP (ORDER BY key_ordinal) AS IncCols
+ FROM sys.index_columns AS IC
+ INNER JOIN
+ sys.columns AS C
+ ON C.object_id = IC.object_id
+ AND C.column_id = IC.column_id
+ WHERE IC.object_id = I.object_id
+ AND IC.index_id = I.index_id
+ GROUP BY IC.object_id, IC.index_id) AS IC
+ WHERE O.name = @Tbl
+ AND (@Ind IS NULL
+ OR I.name = @Ind)
+ AND (@IncludeClustered = 1
+ OR index_id > 1)) AS A;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '@Indexes', @Action = 'Insert', @Rows = @@rowcount;
+ IF @Ind IS NULL
+ SELECT Ind,
+ Txt
+ FROM @Indexes;
+ ELSE
+ SET @Txt = (SELECT Txt
+ FROM @Indexes);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Text = @Txt;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.GetJobs
+@QueueType TINYINT, @JobId BIGINT=NULL, @JobIds BigintList READONLY, @GroupId BIGINT=NULL, @ReturnDefinition BIT=1
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'GetJobs', @Mode AS VARCHAR (100) = 'Q=' + isnull(CONVERT (VARCHAR, @QueueType), 'NULL') + ' J=' + isnull(CONVERT (VARCHAR, @JobId), 'NULL') + ' G=' + isnull(CONVERT (VARCHAR, @GroupId), 'NULL'), @st AS DATETIME = getUTCdate(), @PartitionId AS TINYINT = @JobId % 16;
+BEGIN TRY
+ IF @JobId IS NULL
+ AND @GroupId IS NULL
+ AND NOT EXISTS (SELECT *
+ FROM @JobIds)
+ RAISERROR ('@JobId = NULL and @GroupId = NULL and @JobIds is empty', 18, 127);
+ IF @JobId IS NOT NULL
+ SELECT GroupId,
+ JobId,
+ CASE WHEN @ReturnDefinition = 1 THEN Definition ELSE NULL END AS Definition,
+ Version,
+ Status,
+ Priority,
+ Data,
+ Result,
+ CreateDate,
+ StartDate,
+ EndDate,
+ HeartbeatDate,
+ CancelRequested
+ FROM dbo.JobQueue
+ WHERE QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND JobId = isnull(@JobId, -1)
+ AND Status <> 5;
+ ELSE
+ IF @GroupId IS NOT NULL
+ SELECT GroupId,
+ JobId,
+ CASE WHEN @ReturnDefinition = 1 THEN Definition ELSE NULL END AS Definition,
+ Version,
+ Status,
+ Priority,
+ Data,
+ Result,
+ CreateDate,
+ StartDate,
+ EndDate,
+ HeartbeatDate,
+ CancelRequested
+ FROM dbo.JobQueue WITH (INDEX (IX_QueueType_GroupId))
+ WHERE QueueType = @QueueType
+ AND GroupId = isnull(@GroupId, -1)
+ AND Status <> 5;
+ ELSE
+ SELECT GroupId,
+ JobId,
+ CASE WHEN @ReturnDefinition = 1 THEN Definition ELSE NULL END AS Definition,
+ Version,
+ Status,
+ Priority,
+ Data,
+ Result,
+ CreateDate,
+ StartDate,
+ EndDate,
+ HeartbeatDate,
+ CancelRequested
+ FROM dbo.JobQueue
+ WHERE QueueType = @QueueType
+ AND JobId IN (SELECT Id
+ FROM @JobIds)
+ AND PartitionId = JobId % 16
+ AND Status <> 5;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @@rowcount;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.GetPartitionedTables
+@IncludeNotDisabled BIT=1, @IncludeNotSupported BIT=1
+WITH EXECUTE AS 'dbo'
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'GetPartitionedTables', @Mode AS VARCHAR (200) = 'PS=PartitionScheme_ResourceTypeId D=' + isnull(CONVERT (VARCHAR, @IncludeNotDisabled), 'NULL') + ' S=' + isnull(CONVERT (VARCHAR, @IncludeNotSupported), 'NULL'), @st AS DATETIME = getUTCdate();
+DECLARE @NotSupportedTables TABLE (
+ id INT PRIMARY KEY);
+BEGIN TRY
+ INSERT INTO @NotSupportedTables
+ SELECT DISTINCT O.object_id
+ FROM sys.indexes AS I
+ INNER JOIN
+ sys.objects AS O
+ ON O.object_id = I.object_id
+ WHERE O.type = 'u'
+ AND EXISTS (SELECT *
+ FROM sys.partition_schemes AS PS
+ WHERE PS.data_space_id = I.data_space_id
+ AND name = 'PartitionScheme_ResourceTypeId')
+ AND (NOT EXISTS (SELECT *
+ FROM sys.index_columns AS IC
+ INNER JOIN
+ sys.columns AS C
+ ON C.object_id = IC.object_id
+ AND C.column_id = IC.column_id
+ WHERE IC.object_id = I.object_id
+ AND IC.index_id = I.index_id
+ AND IC.key_ordinal > 0
+ AND IC.is_included_column = 0
+ AND C.name = 'ResourceTypeId')
+ OR EXISTS (SELECT *
+ FROM sys.indexes AS NSI
+ WHERE NSI.object_id = O.object_id
+ AND NOT EXISTS (SELECT *
+ FROM sys.partition_schemes AS PS
+ WHERE PS.data_space_id = NSI.data_space_id
+ AND name = 'PartitionScheme_ResourceTypeId')));
+ SELECT CONVERT (VARCHAR (100), O.name),
+ CONVERT (BIT, CASE WHEN EXISTS (SELECT *
+ FROM @NotSupportedTables AS NSI
+ WHERE NSI.id = O.object_id) THEN 0 ELSE 1 END)
+ FROM sys.indexes AS I
+ INNER JOIN
+ sys.objects AS O
+ ON O.object_id = I.object_id
+ WHERE O.type = 'u'
+ AND I.index_id IN (0, 1)
+ AND EXISTS (SELECT *
+ FROM sys.partition_schemes AS PS
+ WHERE PS.data_space_id = I.data_space_id
+ AND name = 'PartitionScheme_ResourceTypeId')
+ AND EXISTS (SELECT *
+ FROM sys.index_columns AS IC
+ INNER JOIN
+ sys.columns AS C
+ ON C.object_id = I.object_id
+ AND C.column_id = IC.column_id
+ AND IC.is_included_column = 0
+ AND C.name = 'ResourceTypeId')
+ AND (@IncludeNotSupported = 1
+ OR NOT EXISTS (SELECT *
+ FROM @NotSupportedTables AS NSI
+ WHERE NSI.id = O.object_id))
+ AND (@IncludeNotDisabled = 1
+ OR EXISTS (SELECT *
+ FROM sys.indexes AS D
+ WHERE D.object_id = O.object_id
+ AND D.is_disabled = 1))
+ ORDER BY 1;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @@rowcount;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.GetReindexJobById
+@id VARCHAR (64)
+AS
+SET NOCOUNT ON;
+SELECT RawJobRecord,
+ JobVersion
+FROM dbo.ReindexJob
+WHERE Id = @id;
+
+GO
+CREATE PROCEDURE dbo.GetResources
+@ResourceKeys dbo.ResourceKeyList READONLY
+AS
+SET NOCOUNT ON;
+DECLARE @st AS DATETIME = getUTCdate(), @SP AS VARCHAR (100) = 'GetResources', @InputRows AS INT, @NotNullVersionExists AS BIT, @NullVersionExists AS BIT, @MinRT AS SMALLINT, @MaxRT AS SMALLINT;
+SELECT @MinRT = min(ResourceTypeId),
+ @MaxRT = max(ResourceTypeId),
+ @InputRows = count(*),
+ @NotNullVersionExists = max(CASE WHEN Version IS NOT NULL THEN 1 ELSE 0 END),
+ @NullVersionExists = max(CASE WHEN Version IS NULL THEN 1 ELSE 0 END)
+FROM @ResourceKeys;
+DECLARE @Mode AS VARCHAR (100) = 'RT=[' + CONVERT (VARCHAR, @MinRT) + ',' + CONVERT (VARCHAR, @MaxRT) + '] Cnt=' + CONVERT (VARCHAR, @InputRows) + ' NNVE=' + CONVERT (VARCHAR, @NotNullVersionExists) + ' NVE=' + CONVERT (VARCHAR, @NullVersionExists);
+BEGIN TRY
+ IF @NotNullVersionExists = 1
+ IF @NullVersionExists = 0
+ SELECT B.ResourceTypeId,
+ B.ResourceId,
+ ResourceSurrogateId,
+ C.Version,
+ IsDeleted,
+ IsHistory,
+ RawResource,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ FileId,
+ OffsetInFile
+ FROM (SELECT *
+ FROM @ResourceKeys) AS A
+ INNER LOOP JOIN
+ dbo.ResourceIdIntMap AS B WITH (INDEX (U_ResourceIdIntMap_ResourceId_ResourceTypeId))
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN
+ dbo.Resource AS C
+ ON C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceIdInt = B.ResourceIdInt
+ AND C.Version = A.Version
+ OPTION (MAXDOP 1);
+ ELSE
+ SELECT *
+ FROM (SELECT B.ResourceTypeId,
+ B.ResourceId,
+ ResourceSurrogateId,
+ C.Version,
+ IsDeleted,
+ IsHistory,
+ RawResource,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ FileId,
+ OffsetInFile
+ FROM (SELECT *
+ FROM @ResourceKeys
+ WHERE Version IS NOT NULL) AS A
+ INNER LOOP JOIN
+ dbo.ResourceIdIntMap AS B WITH (INDEX (U_ResourceIdIntMap_ResourceId_ResourceTypeId))
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN
+ dbo.Resource AS C
+ ON C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceIdInt = B.ResourceIdInt
+ AND C.Version = A.Version
+ UNION ALL
+ SELECT B.ResourceTypeId,
+ B.ResourceId,
+ C.ResourceSurrogateId,
+ C.Version,
+ IsDeleted,
+ IsHistory,
+ RawResource,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ FileId,
+ OffsetInFile
+ FROM (SELECT *
+ FROM @ResourceKeys
+ WHERE Version IS NULL) AS A
+ INNER LOOP JOIN
+ dbo.ResourceIdIntMap AS B WITH (INDEX (U_ResourceIdIntMap_ResourceId_ResourceTypeId))
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN
+ dbo.CurrentResources AS C
+ ON C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceIdInt = B.ResourceIdInt
+ AND C.IsHistory = 0
+ LEFT OUTER JOIN
+ dbo.RawResources AS D
+ ON D.ResourceTypeId = A.ResourceTypeId
+ AND D.ResourceSurrogateId = C.ResourceSurrogateId) AS A
+ OPTION (MAXDOP 1);
+ ELSE
+ SELECT B.ResourceTypeId,
+ B.ResourceId,
+ C.ResourceSurrogateId,
+ C.Version,
+ IsDeleted,
+ IsHistory,
+ RawResource,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ FileId,
+ OffsetInFile
+ FROM (SELECT *
+ FROM @ResourceKeys) AS A
+ INNER LOOP JOIN
+ dbo.ResourceIdIntMap AS B WITH (INDEX (U_ResourceIdIntMap_ResourceId_ResourceTypeId))
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN
+ dbo.CurrentResources AS C
+ ON C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceIdInt = B.ResourceIdInt
+ LEFT OUTER JOIN
+ dbo.RawResources AS D
+ ON D.ResourceTypeId = A.ResourceTypeId
+ AND D.ResourceSurrogateId = C.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @@rowcount;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.GetResourcesByTransactionId
+@TransactionId BIGINT, @IncludeHistory BIT=0, @ReturnResourceKeysOnly BIT=0
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = object_name(@@procid), @Mode AS VARCHAR (100) = 'T=' + CONVERT (VARCHAR, @TransactionId) + ' H=' + CONVERT (VARCHAR, @IncludeHistory), @st AS DATETIME = getUTCdate();
+BEGIN TRY
+ IF @ReturnResourceKeysOnly = 0
+ SELECT ResourceTypeId,
+ ResourceId,
+ ResourceSurrogateId,
+ Version,
+ IsDeleted,
+ IsHistory,
+ RawResource,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ RequestMethod,
+ FileId,
+ OffsetInFile
+ FROM dbo.Resource
+ WHERE TransactionId = @TransactionId
+ AND (IsHistory = 0
+ OR @IncludeHistory = 1)
+ OPTION (MAXDOP 1);
+ ELSE
+ SELECT ResourceTypeId,
+ ResourceId,
+ ResourceSurrogateId,
+ Version,
+ IsDeleted
+ FROM dbo.Resource
+ WHERE TransactionId = @TransactionId
+ AND (IsHistory = 0
+ OR @IncludeHistory = 1)
+ OPTION (MAXDOP 1);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @@rowcount;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.GetResourcesByTypeAndSurrogateIdRange
+@ResourceTypeId SMALLINT, @StartId BIGINT, @EndId BIGINT, @GlobalEndId BIGINT=NULL, @IncludeHistory BIT=1, @IncludeDeleted BIT=1
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'GetResourcesByTypeAndSurrogateIdRange', @Mode AS VARCHAR (100) = 'RT=' + isnull(CONVERT (VARCHAR, @ResourceTypeId), 'NULL') + ' S=' + isnull(CONVERT (VARCHAR, @StartId), 'NULL') + ' E=' + isnull(CONVERT (VARCHAR, @EndId), 'NULL') + ' GE=' + isnull(CONVERT (VARCHAR, @GlobalEndId), 'NULL') + ' HI=' + isnull(CONVERT (VARCHAR, @IncludeHistory), 'NULL') + ' DE=' + isnull(CONVERT (VARCHAR, @IncludeDeleted), 'NULL'), @st AS DATETIME = getUTCdate(), @DummyTop AS BIGINT = 9223372036854775807, @Rows AS INT;
+BEGIN TRY
+ DECLARE @ResourceIdInts TABLE (
+ ResourceIdInt BIGINT PRIMARY KEY);
+ DECLARE @SurrogateIds TABLE (
+ MaxSurrogateId BIGINT PRIMARY KEY);
+ IF @GlobalEndId IS NOT NULL
+ AND @IncludeHistory = 0
+ BEGIN
+ INSERT INTO @ResourceIdInts
+ SELECT DISTINCT ResourceIdInt
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ AND IsHistory = 1
+ AND (IsDeleted = 0
+ OR @IncludeDeleted = 1)
+ OPTION (MAXDOP 1);
+ IF @@rowcount > 0
+ INSERT INTO @SurrogateIds
+ SELECT ResourceSurrogateId
+ FROM (SELECT ResourceIdInt,
+ ResourceSurrogateId,
+ row_number() OVER (PARTITION BY ResourceIdInt ORDER BY ResourceSurrogateId DESC) AS RowId
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceIdInt IN (SELECT TOP (@DummyTop) ResourceIdInt
+ FROM @ResourceIdInts)
+ AND ResourceSurrogateId BETWEEN @StartId AND @GlobalEndId) AS A
+ WHERE RowId = 1
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ END
+ IF @IncludeHistory = 0
+ SELECT ResourceTypeId,
+ ResourceId,
+ Version,
+ IsDeleted,
+ ResourceSurrogateId,
+ RequestMethod,
+ CONVERT (BIT, 1) AS IsMatch,
+ CONVERT (BIT, 0) AS IsPartial,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ RawResource,
+ FileId,
+ OffsetInFile
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ AND IsHistory = 0
+ AND (IsDeleted = 0
+ OR @IncludeDeleted = 1)
+ UNION ALL
+ SELECT ResourceTypeId,
+ ResourceId,
+ Version,
+ IsDeleted,
+ ResourceSurrogateId,
+ RequestMethod,
+ CONVERT (BIT, 1) AS IsMatch,
+ CONVERT (BIT, 0) AS IsPartial,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ RawResource,
+ FileId,
+ OffsetInFile
+ FROM @SurrogateIds
+ INNER JOIN
+ dbo.Resource
+ ON ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId = MaxSurrogateId
+ WHERE IsHistory = 1
+ AND (IsDeleted = 0
+ OR @IncludeDeleted = 1)
+ OPTION (MAXDOP 1, LOOP JOIN);
+ ELSE
+ SELECT ResourceTypeId,
+ ResourceId,
+ Version,
+ IsDeleted,
+ ResourceSurrogateId,
+ RequestMethod,
+ CONVERT (BIT, 1) AS IsMatch,
+ CONVERT (BIT, 0) AS IsPartial,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ RawResource,
+ FileId,
+ OffsetInFile
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ AND (IsDeleted = 0
+ OR @IncludeDeleted = 1)
+ UNION ALL
+ SELECT ResourceTypeId,
+ ResourceId,
+ Version,
+ IsDeleted,
+ ResourceSurrogateId,
+ RequestMethod,
+ CONVERT (BIT, 1) AS IsMatch,
+ CONVERT (BIT, 0) AS IsPartial,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ RawResource,
+ FileId,
+ OffsetInFile
+ FROM @SurrogateIds
+ INNER JOIN
+ dbo.Resource
+ ON ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId = MaxSurrogateId
+ WHERE IsHistory = 1
+ AND (IsDeleted = 0
+ OR @IncludeDeleted = 1)
+ OPTION (MAXDOP 1, LOOP JOIN);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @@rowcount;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.GetResourceSearchParamStats
+@Table VARCHAR (100)=NULL, @ResourceTypeId SMALLINT=NULL, @SearchParamId SMALLINT=NULL
+WITH EXECUTE AS 'dbo'
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = object_name(@@procid), @Mode AS VARCHAR (200) = 'T=' + isnull(@Table, 'NULL') + ' RT=' + isnull(CONVERT (VARCHAR, @ResourceTypeId), 'NULL') + ' SP=' + isnull(CONVERT (VARCHAR, @SearchParamId), 'NULL'), @st AS DATETIME = getUTCdate();
+BEGIN TRY
+ SELECT T.name AS TableName,
+ S.name AS StatsName,
+ db_name() AS DatabaseName
+ FROM sys.stats AS S
+ INNER JOIN
+ sys.tables AS T
+ ON T.object_id = S.object_id
+ WHERE T.name LIKE '%SearchParam'
+ AND T.name <> 'SearchParam'
+ AND S.name LIKE 'ST[_]%'
+ AND (T.name LIKE @Table
+ OR @Table IS NULL)
+ AND (S.name LIKE '%ResourceTypeId[_]' + CONVERT (VARCHAR, @ResourceTypeId) + '[_]%'
+ OR @ResourceTypeId IS NULL)
+ AND (S.name LIKE '%SearchParamId[_]' + CONVERT (VARCHAR, @SearchParamId)
+ OR @SearchParamId IS NULL);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Rows = @@rowcount, @Start = @st;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.GetResourceSurrogateIdRanges
+@ResourceTypeId SMALLINT, @StartId BIGINT, @EndId BIGINT, @RangeSize INT, @NumberOfRanges INT=100, @Up BIT=1
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'GetResourceSurrogateIdRanges', @Mode AS VARCHAR (100) = 'RT=' + isnull(CONVERT (VARCHAR, @ResourceTypeId), 'NULL') + ' S=' + isnull(CONVERT (VARCHAR, @StartId), 'NULL') + ' E=' + isnull(CONVERT (VARCHAR, @EndId), 'NULL') + ' R=' + isnull(CONVERT (VARCHAR, @RangeSize), 'NULL') + ' UP=' + isnull(CONVERT (VARCHAR, @Up), 'NULL'), @st AS DATETIME = getUTCdate();
+BEGIN TRY
+ IF @Up = 1
+ SELECT RangeId,
+ min(ResourceSurrogateId),
+ max(ResourceSurrogateId),
+ count(*)
+ FROM (SELECT isnull(CONVERT (INT, (row_number() OVER (ORDER BY ResourceSurrogateId) - 1) / @RangeSize), 0) AS RangeId,
+ ResourceSurrogateId
+ FROM (SELECT TOP (@RangeSize * @NumberOfRanges) ResourceSurrogateId
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId >= @StartId
+ AND ResourceSurrogateId <= @EndId
+ ORDER BY ResourceSurrogateId) AS A) AS A
+ GROUP BY RangeId
+ OPTION (MAXDOP 1);
+ ELSE
+ SELECT RangeId,
+ min(ResourceSurrogateId),
+ max(ResourceSurrogateId),
+ count(*)
+ FROM (SELECT isnull(CONVERT (INT, (row_number() OVER (ORDER BY ResourceSurrogateId) - 1) / @RangeSize), 0) AS RangeId,
+ ResourceSurrogateId
+ FROM (SELECT TOP (@RangeSize * @NumberOfRanges) ResourceSurrogateId
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId >= @StartId
+ AND ResourceSurrogateId <= @EndId
+ ORDER BY ResourceSurrogateId DESC) AS A) AS A
+ GROUP BY RangeId
+ OPTION (MAXDOP 1);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @@rowcount;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.GetResourceVersions
+@ResourceDateKeys dbo.ResourceDateKeyList READONLY
+AS
+SET NOCOUNT ON;
+DECLARE @st AS DATETIME = getUTCdate(), @SP AS VARCHAR (100) = 'GetResourceVersions', @Mode AS VARCHAR (100) = 'Rows=' + CONVERT (VARCHAR, (SELECT count(*)
+ FROM @ResourceDateKeys)), @DummyTop AS BIGINT = 9223372036854775807;
+BEGIN TRY
+ SELECT A.ResourceTypeId,
+ A.ResourceId,
+ A.ResourceSurrogateId,
+ CASE WHEN D.Version IS NOT NULL THEN 0 WHEN isnull(U.Version, 1) - isnull(L.Version, 0) > ResourceIndex THEN isnull(U.Version, 1) - ResourceIndex ELSE isnull(M.Version, 0) - ResourceIndex END AS Version,
+ isnull(D.Version, 0) AS MatchedVersion,
+ D.RawResource AS MatchedRawResource,
+ D.FileId AS MatchedFileId,
+ D.OffsetInFile AS MatchedOffsetInFile
+ FROM (SELECT TOP (@DummyTop) A.*,
+ M.ResourceIdInt,
+ CONVERT (INT, row_number() OVER (PARTITION BY A.ResourceTypeId, A.ResourceId ORDER BY ResourceSurrogateId DESC)) AS ResourceIndex
+ FROM @ResourceDateKeys AS A
+ LEFT OUTER JOIN
+ dbo.ResourceIdIntMap AS M WITH (INDEX (U_ResourceIdIntMap_ResourceId_ResourceTypeId))
+ ON M.ResourceTypeId = A.ResourceTypeId
+ AND M.ResourceId = A.ResourceId) AS A OUTER APPLY (SELECT TOP 1 *
+ FROM dbo.Resource AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt
+ AND B.Version > 0
+ AND B.ResourceSurrogateId < A.ResourceSurrogateId
+ ORDER BY B.ResourceSurrogateId DESC) AS L OUTER APPLY (SELECT TOP 1 *
+ FROM dbo.Resource AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt
+ AND B.Version > 0
+ AND B.ResourceSurrogateId > A.ResourceSurrogateId
+ ORDER BY B.ResourceSurrogateId) AS U OUTER APPLY (SELECT TOP 1 *
+ FROM dbo.Resource AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt
+ AND B.Version < 0
+ ORDER BY B.Version) AS M OUTER APPLY (SELECT TOP 1 *
+ FROM dbo.Resource AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt
+ AND B.ResourceSurrogateId BETWEEN A.ResourceSurrogateId AND A.ResourceSurrogateId + 79999) AS D
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @@rowcount;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.GetSearchParamStatuses
+AS
+SET NOCOUNT ON;
+SELECT SearchParamId,
+ Uri,
+ Status,
+ LastUpdated,
+ IsPartiallySupported
+FROM dbo.SearchParam;
+
+GO
+CREATE PROCEDURE dbo.GetTransactions
+@StartNotInclusiveTranId BIGINT, @EndInclusiveTranId BIGINT, @EndDate DATETIME=NULL
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = object_name(@@procid), @Mode AS VARCHAR (100) = 'ST=' + CONVERT (VARCHAR, @StartNotInclusiveTranId) + ' ET=' + CONVERT (VARCHAR, @EndInclusiveTranId) + ' ED=' + isnull(CONVERT (VARCHAR, @EndDate, 121), 'NULL'), @st AS DATETIME = getUTCdate();
+IF @EndDate IS NULL
+ SET @EndDate = getUTCdate();
+SELECT SurrogateIdRangeFirstValue,
+ VisibleDate,
+ InvisibleHistoryRemovedDate
+FROM dbo.Transactions
+WHERE SurrogateIdRangeFirstValue > @StartNotInclusiveTranId
+ AND SurrogateIdRangeFirstValue <= @EndInclusiveTranId
+ AND EndDate <= @EndDate
+ORDER BY SurrogateIdRangeFirstValue;
+EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @@rowcount;
+
+GO
+CREATE PROCEDURE dbo.GetUsedResourceTypes
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'GetUsedResourceTypes', @Mode AS VARCHAR (100) = '', @st AS DATETIME = getUTCdate();
+BEGIN TRY
+ SELECT ResourceTypeId,
+ Name
+ FROM dbo.ResourceType AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.Resource AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @@rowcount;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.HardDeleteResource
+@ResourceTypeId SMALLINT, @ResourceId VARCHAR (64), @KeepCurrentVersion BIT, @IsResourceChangeCaptureEnabled BIT=0, @MakeResourceInvisible BIT=0
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = object_name(@@procid), @Mode AS VARCHAR (200) = 'RT=' + CONVERT (VARCHAR, @ResourceTypeId) + ' R=' + @ResourceId + ' V=' + CONVERT (VARCHAR, @KeepCurrentVersion), @st AS DATETIME = getUTCdate(), @TransactionId AS BIGINT, @DeletedIdMap AS INT = 0, @Rows AS INT;
+IF @IsResourceChangeCaptureEnabled = 1
+ SET @MakeResourceInvisible = 1;
+SET @Mode += ' I=' + CONVERT (VARCHAR, @MakeResourceInvisible);
+IF @MakeResourceInvisible = 1
+ BEGIN
+ EXECUTE dbo.MergeResourcesBeginTransaction @Count = 1, @TransactionId = @TransactionId OUTPUT;
+ SET @Mode += ' T=' + CONVERT (VARCHAR, @TransactionId);
+ END
+DECLARE @Ids TABLE (
+ ResourceSurrogateId BIGINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL);
+DECLARE @IdsDistinct TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL PRIMARY KEY (ResourceTypeId, ResourceIdInt));
+DECLARE @RefIdsRaw TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL);
+RetryResourceIdIntMapLogic:
+BEGIN TRY
+ BEGIN TRANSACTION;
+ IF @MakeResourceInvisible = 1
+ UPDATE dbo.Resource
+ SET IsDeleted = 1,
+ RawResource = 0xF,
+ SearchParamHash = NULL,
+ HistoryTransactionId = @TransactionId
+ OUTPUT deleted.ResourceSurrogateId, deleted.ResourceIdInt INTO @Ids
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceId = @ResourceId
+ AND (@KeepCurrentVersion = 0
+ OR IsHistory = 1)
+ AND (RawResource IS NULL
+ OR RawResource <> 0xF);
+ ELSE
+ BEGIN
+ DELETE dbo.Resource
+ OUTPUT deleted.ResourceSurrogateId, deleted.ResourceIdInt INTO @Ids
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceId = @ResourceId
+ AND (@KeepCurrentVersion = 0
+ OR IsHistory = 1)
+ AND RawResource <> 0xF;
+ INSERT INTO @IdsDistinct
+ SELECT DISTINCT @ResourceTypeId,
+ ResourceIdInt
+ FROM @Ids;
+ SET @Rows = @@rowcount;
+ IF @Rows > 0
+ BEGIN
+ DELETE A
+ FROM @IdsDistinct AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.CurrentResources AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt);
+ SET @Rows -= @@rowcount;
+ IF @Rows > 0
+ BEGIN
+ DELETE A
+ FROM @IdsDistinct AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.ResourceReferenceSearchParams AS B
+ WHERE B.ReferenceResourceTypeId = A.ResourceTypeId
+ AND B.ReferenceResourceIdInt = A.ResourceIdInt);
+ SET @Rows -= @@rowcount;
+ IF @Rows > 0
+ BEGIN
+ DELETE B
+ FROM @IdsDistinct AS A
+ INNER LOOP JOIN
+ dbo.ResourceIdIntMap AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt;
+ SET @DeletedIdMap = @@rowcount;
+ END
+ END
+ END
+ END
+ IF @KeepCurrentVersion = 0
+ BEGIN
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.ResourceWriteClaim AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ OUTPUT deleted.ReferenceResourceTypeId, deleted.ReferenceResourceIdInt INTO @RefIdsRaw
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.ResourceReferenceSearchParams AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE @IdsDistinct;
+ INSERT INTO @IdsDistinct
+ SELECT DISTINCT ResourceTypeId,
+ ResourceIdInt
+ FROM @RefIdsRaw;
+ SET @Rows = @@rowcount;
+ IF @Rows > 0
+ BEGIN
+ DELETE A
+ FROM @IdsDistinct AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.CurrentResources AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt);
+ SET @Rows -= @@rowcount;
+ IF @Rows > 0
+ BEGIN
+ DELETE A
+ FROM @IdsDistinct AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.ResourceReferenceSearchParams AS B
+ WHERE B.ReferenceResourceTypeId = A.ResourceTypeId
+ AND B.ReferenceResourceIdInt = A.ResourceIdInt);
+ SET @Rows -= @@rowcount;
+ IF @Rows > 0
+ BEGIN
+ DELETE B
+ FROM @IdsDistinct AS A
+ INNER LOOP JOIN
+ dbo.ResourceIdIntMap AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt;
+ SET @DeletedIdMap += @@rowcount;
+ END
+ END
+ END
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.StringReferenceSearchParams AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenSearchParam AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenText AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.StringSearchParam AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.UriSearchParam AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.NumberSearchParam AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.QuantitySearchParam AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.DateTimeSearchParam AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.ReferenceTokenCompositeSearchParam AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenTokenCompositeSearchParam AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenDateTimeCompositeSearchParam AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenQuantityCompositeSearchParam AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenStringCompositeSearchParam AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenNumberNumberCompositeSearchParam AS B WITH (INDEX (1), FORCESEEK, PAGLOCK)
+ ON B.ResourceTypeId = @ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ OPTION (MAXDOP 1);
+ END
+ COMMIT TRANSACTION;
+ IF @MakeResourceInvisible = 1
+ EXECUTE dbo.MergeResourcesCommitTransaction @TransactionId;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Text = @DeletedIdMap;
+END TRY
+BEGIN CATCH
+ IF @@trancount > 0
+ ROLLBACK;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ IF error_number() = 547
+ AND error_message() LIKE '%DELETE%'
+ BEGIN
+ DELETE @Ids;
+ DELETE @RefIdsRaw;
+ DELETE @IdsDistinct;
+ GOTO RetryResourceIdIntMapLogic;
+ END
+ ELSE
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.InitializeIndexProperties
+AS
+SET NOCOUNT ON;
+INSERT INTO dbo.IndexProperties (TableName, IndexName, PropertyName, PropertyValue)
+SELECT Tbl,
+ Ind,
+ 'DATA_COMPRESSION',
+ isnull(data_comp, 'NONE')
+FROM (SELECT O.Name AS Tbl,
+ I.Name AS Ind,
+ (SELECT TOP 1 CASE WHEN data_compression_desc = 'PAGE' THEN 'PAGE' END
+ FROM sys.partitions AS P
+ WHERE P.object_id = I.object_id
+ AND I.index_id = P.index_id) AS data_comp
+ FROM sys.indexes AS I
+ INNER JOIN
+ sys.objects AS O
+ ON O.object_id = I.object_id
+ WHERE O.type = 'u'
+ AND EXISTS (SELECT *
+ FROM sys.partition_schemes AS PS
+ WHERE PS.data_space_id = I.data_space_id
+ AND name = 'PartitionScheme_ResourceTypeId')) AS A
+WHERE NOT EXISTS (SELECT *
+ FROM dbo.IndexProperties
+ WHERE TableName = Tbl
+ AND IndexName = Ind);
+
+GO
+CREATE PROCEDURE dbo.LogEvent
+@Process VARCHAR (100), @Status VARCHAR (10), @Mode VARCHAR (200)=NULL, @Action VARCHAR (20)=NULL, @Target VARCHAR (100)=NULL, @Rows BIGINT=NULL, @Start DATETIME=NULL, @Text NVARCHAR (3500)=NULL, @EventId BIGINT=NULL OUTPUT, @Retry INT=NULL
+AS
+SET NOCOUNT ON;
+DECLARE @ErrorNumber AS INT = error_number(), @ErrorMessage AS VARCHAR (1000) = '', @TranCount AS INT = @@trancount, @DoWork AS BIT = 0, @NumberAdded AS BIT;
+IF @ErrorNumber IS NOT NULL
+ OR @Status IN ('Warn', 'Error')
+ SET @DoWork = 1;
+IF @DoWork = 0
+ SET @DoWork = CASE WHEN EXISTS (SELECT *
+ FROM dbo.Parameters
+ WHERE Id = isnull(@Process, '')
+ AND Char = 'LogEvent') THEN 1 ELSE 0 END;
+IF @DoWork = 0
+ RETURN;
+IF @ErrorNumber IS NOT NULL
+ SET @ErrorMessage = CASE WHEN @Retry IS NOT NULL THEN 'Retry ' + CONVERT (VARCHAR, @Retry) + ', ' ELSE '' END + 'Error ' + CONVERT (VARCHAR, error_number()) + ': ' + CONVERT (VARCHAR (1000), error_message()) + ', Level ' + CONVERT (VARCHAR, error_severity()) + ', State ' + CONVERT (VARCHAR, error_state()) + CASE WHEN error_procedure() IS NOT NULL THEN ', Procedure ' + error_procedure() ELSE '' END + ', Line ' + CONVERT (VARCHAR, error_line());
+IF @TranCount > 0
+ AND @ErrorNumber IS NOT NULL
+ ROLLBACK;
+IF databasepropertyex(db_name(), 'UpdateAbility') = 'READ_WRITE'
+ BEGIN
+ INSERT INTO dbo.EventLog (Process, Status, Mode, Action, Target, Rows, Milliseconds, EventDate, EventText, SPID, HostName)
+ SELECT @Process,
+ @Status,
+ @Mode,
+ @Action,
+ @Target,
+ @Rows,
+ datediff(millisecond, @Start, getUTCdate()),
+ getUTCdate() AS EventDate,
+ CASE WHEN @ErrorNumber IS NULL THEN @Text ELSE @ErrorMessage + CASE WHEN isnull(@Text, '') <> '' THEN '. ' + @Text ELSE '' END END AS Text,
+ @@SPID,
+ host_name() AS HostName;
+ SET @EventId = scope_identity();
+ END
+IF @TranCount > 0
+ AND @ErrorNumber IS NOT NULL
+ BEGIN TRANSACTION;
+
+GO
+CREATE PROCEDURE dbo.LogSchemaMigrationProgress
+@message VARCHAR (MAX)
+AS
+INSERT INTO dbo.SchemaMigrationProgress (Message)
+VALUES (@message);
+
+GO
+CREATE PROCEDURE dbo.MergeResources
+@AffectedRows INT=0 OUTPUT, @RaiseExceptionOnConflict BIT=1, @IsResourceChangeCaptureEnabled BIT=0, @TransactionId BIGINT=NULL, @SingleTransaction BIT=1, @Resources dbo.ResourceList READONLY, @ResourcesLake dbo.ResourceListLake READONLY, @ResourceWriteClaims dbo.ResourceWriteClaimList READONLY, @ReferenceSearchParams dbo.ReferenceSearchParamList READONLY, @TokenSearchParams dbo.TokenSearchParamList READONLY, @TokenTexts dbo.TokenTextList READONLY, @StringSearchParams dbo.StringSearchParamList READONLY, @UriSearchParams dbo.UriSearchParamList READONLY, @NumberSearchParams dbo.NumberSearchParamList READONLY, @QuantitySearchParams dbo.QuantitySearchParamList READONLY, @DateTimeSearchParms dbo.DateTimeSearchParamList READONLY, @ReferenceTokenCompositeSearchParams dbo.ReferenceTokenCompositeSearchParamList READONLY, @TokenTokenCompositeSearchParams dbo.TokenTokenCompositeSearchParamList READONLY, @TokenDateTimeCompositeSearchParams dbo.TokenDateTimeCompositeSearchParamList READONLY, @TokenQuantityCompositeSearchParams dbo.TokenQuantityCompositeSearchParamList READONLY, @TokenStringCompositeSearchParams dbo.TokenStringCompositeSearchParamList READONLY, @TokenNumberNumberCompositeSearchParams dbo.TokenNumberNumberCompositeSearchParamList READONLY
+AS
+SET NOCOUNT ON;
+DECLARE @st AS DATETIME = getUTCdate(), @SP AS VARCHAR (100) = object_name(@@procid), @DummyTop AS BIGINT = 9223372036854775807, @InitialTranCount AS INT = @@trancount, @IsRetry AS BIT = 0, @RT AS SMALLINT, @NewIdsCount AS INT, @FirstIdInt AS BIGINT, @CurrentRows AS INT, @DeletedIdMap AS INT;
+DECLARE @Mode AS VARCHAR (200) = isnull((SELECT 'RT=[' + CONVERT (VARCHAR, min(ResourceTypeId)) + ',' + CONVERT (VARCHAR, max(ResourceTypeId)) + '] Sur=[' + CONVERT (VARCHAR, min(ResourceSurrogateId)) + ',' + CONVERT (VARCHAR, max(ResourceSurrogateId)) + '] V=' + CONVERT (VARCHAR, max(Version)) + ' Rows=' + CONVERT (VARCHAR, count(*))
+ FROM (SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ Version
+ FROM @Resources
+ UNION ALL
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ Version
+ FROM @ResourcesLake) AS A), 'Input=Empty');
+SET @Mode += ' E=' + CONVERT (VARCHAR, @RaiseExceptionOnConflict) + ' CC=' + CONVERT (VARCHAR, @IsResourceChangeCaptureEnabled) + ' IT=' + CONVERT (VARCHAR, @InitialTranCount) + ' T=' + isnull(CONVERT (VARCHAR, @TransactionId), 'NULL');
+SET @AffectedRows = 0;
+RetryResourceIdIntMapLogic:
+BEGIN TRY
+ DECLARE @InputIds AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId));
+ DECLARE @CurrentRefIdsRaw TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL);
+ DECLARE @CurrentRefIds TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL PRIMARY KEY (ResourceTypeId, ResourceIdInt));
+ DECLARE @ExistingIdsReference AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId));
+ DECLARE @ExistingIdsResource AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId));
+ DECLARE @InsertIds AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ IdIndex INT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId));
+ DECLARE @InsertedIdsReference AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId));
+ DECLARE @InsertedIdsResource AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId));
+ DECLARE @ResourcesWithIds AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ ResourceIdInt BIGINT NOT NULL,
+ Version INT NOT NULL,
+ HasVersionToCompare BIT NOT NULL,
+ IsDeleted BIT NOT NULL,
+ IsHistory BIT NOT NULL,
+ KeepHistory BIT NOT NULL,
+ RawResource VARBINARY (MAX) NULL,
+ IsRawResourceMetaSet BIT NOT NULL,
+ RequestMethod VARCHAR (10) NULL,
+ SearchParamHash VARCHAR (64) NULL,
+ FileId BIGINT NULL,
+ OffsetInFile INT NULL PRIMARY KEY (ResourceTypeId, ResourceSurrogateId),
+ UNIQUE (ResourceTypeId, ResourceIdInt, Version));
+ DECLARE @ReferenceSearchParamsWithIds AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ BaseUri VARCHAR (128) COLLATE Latin1_General_100_CS_AS NULL,
+ ReferenceResourceTypeId SMALLINT NOT NULL,
+ ReferenceResourceIdInt BIGINT NOT NULL UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt));
+ INSERT INTO @InputIds
+ SELECT DISTINCT ReferenceResourceTypeId,
+ ReferenceResourceId
+ FROM @ReferenceSearchParams
+ WHERE ReferenceResourceTypeId IS NOT NULL;
+ INSERT INTO @ExistingIdsReference (ResourceTypeId, ResourceIdInt, ResourceId)
+ SELECT A.ResourceTypeId,
+ ResourceIdInt,
+ A.ResourceId
+ FROM @InputIds AS A
+ INNER JOIN
+ dbo.ResourceIdIntMap AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceId = A.ResourceId;
+ INSERT INTO @InsertIds (ResourceTypeId, IdIndex, ResourceId)
+ SELECT ResourceTypeId,
+ row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1,
+ ResourceId
+ FROM @InputIds AS A
+ WHERE NOT EXISTS (SELECT *
+ FROM @ExistingIdsReference AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceId = A.ResourceId);
+ SET @NewIdsCount = (SELECT count(*)
+ FROM @InsertIds);
+ IF @NewIdsCount > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @NewIdsCount, @FirstIdInt OUTPUT;
+ INSERT INTO @InsertedIdsReference (ResourceTypeId, ResourceIdInt, ResourceId)
+ SELECT ResourceTypeId,
+ IdIndex + @FirstIdInt,
+ ResourceId
+ FROM @InsertIds;
+ END
+ INSERT INTO @ReferenceSearchParamsWithIds (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt)
+ SELECT A.ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ BaseUri,
+ ReferenceResourceTypeId,
+ isnull(C.ResourceIdInt, B.ResourceIdInt)
+ FROM @ReferenceSearchParams AS A
+ LEFT OUTER JOIN
+ @InsertedIdsReference AS B
+ ON B.ResourceTypeId = A.ReferenceResourceTypeId
+ AND B.ResourceId = A.ReferenceResourceId
+ LEFT OUTER JOIN
+ @ExistingIdsReference AS C
+ ON C.ResourceTypeId = A.ReferenceResourceTypeId
+ AND C.ResourceId = A.ReferenceResourceId
+ WHERE ReferenceResourceTypeId IS NOT NULL;
+ DELETE @InputIds;
+ IF EXISTS (SELECT *
+ FROM @ResourcesLake)
+ INSERT INTO @InputIds
+ SELECT ResourceTypeId,
+ ResourceId
+ FROM @ResourcesLake
+ GROUP BY ResourceTypeId, ResourceId;
+ ELSE
+ INSERT INTO @InputIds
+ SELECT ResourceTypeId,
+ ResourceId
+ FROM @Resources
+ GROUP BY ResourceTypeId, ResourceId;
+ INSERT INTO @ExistingIdsResource (ResourceTypeId, ResourceIdInt, ResourceId)
+ SELECT A.ResourceTypeId,
+ isnull(C.ResourceIdInt, B.ResourceIdInt),
+ A.ResourceId
+ FROM @InputIds AS A
+ LEFT OUTER JOIN
+ dbo.ResourceIdIntMap AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN
+ @InsertedIdsReference AS C
+ ON C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceId = A.ResourceId
+ WHERE C.ResourceIdInt IS NOT NULL
+ OR B.ResourceIdInt IS NOT NULL;
+ DELETE @InsertIds;
+ INSERT INTO @InsertIds (ResourceTypeId, IdIndex, ResourceId)
+ SELECT ResourceTypeId,
+ row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1,
+ ResourceId
+ FROM @InputIds AS A
+ WHERE NOT EXISTS (SELECT *
+ FROM @ExistingIdsResource AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceId = A.ResourceId);
+ SET @NewIdsCount = (SELECT count(*)
+ FROM @InsertIds);
+ IF @NewIdsCount > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @NewIdsCount, @FirstIdInt OUTPUT;
+ INSERT INTO @InsertedIdsResource (ResourceTypeId, ResourceIdInt, ResourceId)
+ SELECT ResourceTypeId,
+ IdIndex + @FirstIdInt,
+ ResourceId
+ FROM @InsertIds;
+ END
+ IF EXISTS (SELECT *
+ FROM @ResourcesLake)
+ INSERT INTO @ResourcesWithIds (ResourceTypeId, ResourceId, ResourceIdInt, Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, FileId, OffsetInFile)
+ SELECT A.ResourceTypeId,
+ A.ResourceId,
+ isnull(C.ResourceIdInt, B.ResourceIdInt),
+ Version,
+ HasVersionToCompare,
+ IsHistory,
+ ResourceSurrogateId,
+ IsDeleted,
+ RequestMethod,
+ KeepHistory,
+ RawResource,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ FileId,
+ OffsetInFile
+ FROM @ResourcesLake AS A
+ LEFT OUTER JOIN
+ @InsertedIdsResource AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN
+ @ExistingIdsResource AS C
+ ON C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceId = A.ResourceId;
+ ELSE
+ INSERT INTO @ResourcesWithIds (ResourceTypeId, ResourceId, ResourceIdInt, Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, FileId, OffsetInFile)
+ SELECT A.ResourceTypeId,
+ A.ResourceId,
+ isnull(C.ResourceIdInt, B.ResourceIdInt),
+ Version,
+ HasVersionToCompare,
+ IsHistory,
+ ResourceSurrogateId,
+ IsDeleted,
+ RequestMethod,
+ KeepHistory,
+ RawResource,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ NULL,
+ NULL
+ FROM @Resources AS A
+ LEFT OUTER JOIN
+ @InsertedIdsResource AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN
+ @ExistingIdsResource AS C
+ ON C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceId = A.ResourceId;
+ DECLARE @Existing AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ SurrogateId BIGINT NOT NULL PRIMARY KEY (ResourceTypeId, SurrogateId));
+ DECLARE @ResourceInfos AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ SurrogateId BIGINT NOT NULL,
+ Version INT NOT NULL,
+ KeepHistory BIT NOT NULL,
+ PreviousVersion INT NULL,
+ PreviousSurrogateId BIGINT NULL PRIMARY KEY (ResourceTypeId, SurrogateId));
+ DECLARE @PreviousSurrogateIds AS TABLE (
+ TypeId SMALLINT NOT NULL,
+ SurrogateId BIGINT NOT NULL PRIMARY KEY (TypeId, SurrogateId),
+ KeepHistory BIT );
+ IF @SingleTransaction = 0
+ AND isnull((SELECT Number
+ FROM dbo.Parameters
+ WHERE Id = 'MergeResources.NoTransaction.IsEnabled'), 0) = 0
+ SET @SingleTransaction = 1;
+ SET @Mode += ' ST=' + CONVERT (VARCHAR, @SingleTransaction);
+ IF @InitialTranCount = 0
+ BEGIN
+ IF EXISTS (SELECT *
+ FROM @ResourcesWithIds AS A
+ INNER JOIN
+ dbo.Resource AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ WHERE B.IsHistory = 0)
+ BEGIN
+ BEGIN TRANSACTION;
+ INSERT INTO @Existing (ResourceTypeId, SurrogateId)
+ SELECT B.ResourceTypeId,
+ B.ResourceSurrogateId
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @ResourcesWithIds) AS A
+ INNER JOIN
+ dbo.Resource AS B WITH (ROWLOCK, HOLDLOCK)
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ WHERE B.IsHistory = 0
+ AND B.ResourceId = A.ResourceId
+ AND B.Version = A.Version
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ IF @@rowcount = (SELECT count(*)
+ FROM @ResourcesWithIds)
+ SET @IsRetry = 1;
+ IF @IsRetry = 0
+ COMMIT TRANSACTION;
+ END
+ END
+ SET @Mode += ' R=' + CONVERT (VARCHAR, @IsRetry);
+ IF @SingleTransaction = 1
+ AND @@trancount = 0
+ BEGIN TRANSACTION;
+ IF @IsRetry = 0
+ BEGIN
+ INSERT INTO @ResourceInfos (ResourceTypeId, SurrogateId, Version, KeepHistory, PreviousVersion, PreviousSurrogateId)
+ SELECT A.ResourceTypeId,
+ A.ResourceSurrogateId,
+ A.Version,
+ A.KeepHistory,
+ B.Version,
+ B.ResourceSurrogateId
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @ResourcesWithIds
+ WHERE HasVersionToCompare = 1) AS A
+ LEFT OUTER JOIN
+ dbo.CurrentResources AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ IF @RaiseExceptionOnConflict = 1
+ AND EXISTS (SELECT *
+ FROM @ResourceInfos
+ WHERE PreviousVersion IS NOT NULL
+ AND Version <= PreviousVersion)
+ THROW 50409, 'Resource has been recently updated or added, please compare the resource content in code for any duplicate updates', 1;
+ INSERT INTO @PreviousSurrogateIds
+ SELECT ResourceTypeId,
+ PreviousSurrogateId,
+ KeepHistory
+ FROM @ResourceInfos
+ WHERE PreviousSurrogateId IS NOT NULL;
+ IF @@rowcount > 0
+ BEGIN
+ UPDATE dbo.Resource
+ SET IsHistory = 1
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId
+ AND KeepHistory = 1);
+ SET @AffectedRows += @@rowcount;
+ IF @IsResourceChangeCaptureEnabled = 1
+ AND NOT EXISTS (SELECT *
+ FROM dbo.Parameters
+ WHERE Id = 'InvisibleHistory.IsEnabled'
+ AND Number = 0)
+ UPDATE dbo.Resource
+ SET IsHistory = 1,
+ RawResource = 0xF,
+ SearchParamHash = NULL,
+ HistoryTransactionId = @TransactionId
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId
+ AND KeepHistory = 0);
+ ELSE
+ DELETE dbo.Resource
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId
+ AND KeepHistory = 0);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.ResourceWriteClaim
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.ResourceReferenceSearchParams
+ OUTPUT deleted.ReferenceResourceTypeId, deleted.ReferenceResourceIdInt INTO @CurrentRefIdsRaw
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @CurrentRows = @@rowcount;
+ SET @AffectedRows += @CurrentRows;
+ INSERT INTO @CurrentRefIds
+ SELECT DISTINCT ResourceTypeId,
+ ResourceIdInt
+ FROM @CurrentRefIdsRaw;
+ SET @CurrentRows = @@rowcount;
+ IF @CurrentRows > 0
+ BEGIN
+ DELETE A
+ FROM @CurrentRefIds AS A
+ WHERE EXISTS (SELECT *
+ FROM @ReferenceSearchParamsWithIds AS B
+ WHERE B.ReferenceResourceTypeId = A.ResourceTypeId
+ AND B.ReferenceResourceIdInt = A.ResourceIdInt);
+ SET @CurrentRows -= @@rowcount;
+ IF @CurrentRows > 0
+ BEGIN
+ DELETE A
+ FROM @CurrentRefIds AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.CurrentResources AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt);
+ SET @CurrentRows -= @@rowcount;
+ IF @CurrentRows > 0
+ BEGIN
+ DELETE A
+ FROM @CurrentRefIds AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.ResourceReferenceSearchParams AS B
+ WHERE B.ReferenceResourceTypeId = A.ResourceTypeId
+ AND B.ReferenceResourceIdInt = A.ResourceIdInt);
+ SET @CurrentRows -= @@rowcount;
+ IF @CurrentRows > 0
+ BEGIN
+ DELETE B
+ FROM @CurrentRefIds AS A
+ INNER LOOP JOIN
+ dbo.ResourceIdIntMap AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt;
+ SET @DeletedIdMap = @@rowcount;
+ END
+ END
+ END
+ END
+ DELETE dbo.StringReferenceSearchParams
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.TokenSearchParam
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.TokenText
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.StringSearchParam
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.UriSearchParam
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.NumberSearchParam
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.QuantitySearchParam
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.DateTimeSearchParam
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.ReferenceTokenCompositeSearchParam
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.TokenTokenCompositeSearchParam
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.TokenDateTimeCompositeSearchParam
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.TokenQuantityCompositeSearchParam
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.TokenStringCompositeSearchParam
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ DELETE dbo.TokenNumberNumberCompositeSearchParam
+ WHERE EXISTS (SELECT *
+ FROM @PreviousSurrogateIds
+ WHERE TypeId = ResourceTypeId
+ AND SurrogateId = ResourceSurrogateId);
+ SET @AffectedRows += @@rowcount;
+ END
+ INSERT INTO dbo.ResourceIdIntMap (ResourceTypeId, ResourceIdInt, ResourceId)
+ SELECT ResourceTypeId,
+ ResourceIdInt,
+ ResourceId
+ FROM @InsertedIdsResource;
+ INSERT INTO dbo.ResourceIdIntMap (ResourceTypeId, ResourceIdInt, ResourceId)
+ SELECT ResourceTypeId,
+ ResourceIdInt,
+ ResourceId
+ FROM @InsertedIdsReference;
+ INSERT INTO dbo.Resource (ResourceTypeId, ResourceIdInt, Version, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, RawResource, IsRawResourceMetaSet, SearchParamHash, TransactionId, FileId, OffsetInFile)
+ SELECT ResourceTypeId,
+ ResourceIdInt,
+ Version,
+ IsHistory,
+ ResourceSurrogateId,
+ IsDeleted,
+ RequestMethod,
+ RawResource,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ @TransactionId,
+ FileId,
+ OffsetInFile
+ FROM @ResourcesWithIds;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.ResourceWriteClaim (ResourceSurrogateId, ClaimTypeId, ClaimValue)
+ SELECT ResourceSurrogateId,
+ ClaimTypeId,
+ ClaimValue
+ FROM @ResourceWriteClaims;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.ResourceReferenceSearchParams (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ BaseUri,
+ ReferenceResourceTypeId,
+ ReferenceResourceIdInt
+ FROM @ReferenceSearchParamsWithIds;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.StringReferenceSearchParams (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ BaseUri,
+ ReferenceResourceId
+ FROM @ReferenceSearchParams
+ WHERE ReferenceResourceTypeId IS NULL;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId,
+ Code,
+ CodeOverflow
+ FROM @TokenSearchParams;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenText (ResourceTypeId, ResourceSurrogateId, SearchParamId, Text)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ Text
+ FROM @TokenTexts;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.StringSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ Text,
+ TextOverflow,
+ IsMin,
+ IsMax
+ FROM @StringSearchParams;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.UriSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ Uri
+ FROM @UriSearchParams;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.NumberSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SingleValue,
+ LowValue,
+ HighValue
+ FROM @NumberSearchParams;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.QuantitySearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId,
+ QuantityCodeId,
+ SingleValue,
+ LowValue,
+ HighValue
+ FROM @QuantitySearchParams;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.DateTimeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ StartDateTime,
+ EndDateTime,
+ IsLongerThanADay,
+ IsMin,
+ IsMax
+ FROM @DateTimeSearchParms;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.ReferenceTokenCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ BaseUri1,
+ ReferenceResourceTypeId1,
+ ReferenceResourceId1,
+ ReferenceResourceVersion1,
+ SystemId2,
+ Code2,
+ CodeOverflow2
+ FROM @ReferenceTokenCompositeSearchParams;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenTokenCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ SystemId2,
+ Code2,
+ CodeOverflow2
+ FROM @TokenTokenCompositeSearchParams;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenDateTimeCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ StartDateTime2,
+ EndDateTime2,
+ IsLongerThanADay2
+ FROM @TokenDateTimeCompositeSearchParams;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenQuantityCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ SingleValue2,
+ SystemId2,
+ QuantityCodeId2,
+ LowValue2,
+ HighValue2
+ FROM @TokenQuantityCompositeSearchParams;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenStringCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ Text2,
+ TextOverflow2
+ FROM @TokenStringCompositeSearchParams;
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenNumberNumberCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ SingleValue2,
+ LowValue2,
+ HighValue2,
+ SingleValue3,
+ LowValue3,
+ HighValue3,
+ HasRange
+ FROM @TokenNumberNumberCompositeSearchParams;
+ SET @AffectedRows += @@rowcount;
+ END
+ ELSE
+ BEGIN
+ INSERT INTO dbo.ResourceWriteClaim (ResourceSurrogateId, ClaimTypeId, ClaimValue)
+ SELECT ResourceSurrogateId,
+ ClaimTypeId,
+ ClaimValue
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @ResourceWriteClaims) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.ResourceWriteClaim AS C
+ WHERE C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.ResourceReferenceSearchParams (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt)
+ SELECT A.ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ BaseUri,
+ ReferenceResourceTypeId,
+ ReferenceResourceIdInt
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @ReferenceSearchParamsWithIds) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.ResourceReferenceSearchParams AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.StringReferenceSearchParams (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId)
+ SELECT A.ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ BaseUri,
+ ReferenceResourceId
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @ReferenceSearchParams
+ WHERE ReferenceResourceTypeId IS NULL) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.StringReferenceSearchParams AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId,
+ Code,
+ CodeOverflow
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @TokenSearchParams) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.TokenSearchParam AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenText (ResourceTypeId, ResourceSurrogateId, SearchParamId, Text)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ Text
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @TokenTexts) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.TokenSearchParam AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.StringSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ Text,
+ TextOverflow,
+ IsMin,
+ IsMax
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @StringSearchParams) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.TokenText AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.UriSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ Uri
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @UriSearchParams) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.UriSearchParam AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.NumberSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SingleValue,
+ LowValue,
+ HighValue
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @NumberSearchParams) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.NumberSearchParam AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.QuantitySearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId,
+ QuantityCodeId,
+ SingleValue,
+ LowValue,
+ HighValue
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @QuantitySearchParams) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.QuantitySearchParam AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.DateTimeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ StartDateTime,
+ EndDateTime,
+ IsLongerThanADay,
+ IsMin,
+ IsMax
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @DateTimeSearchParms) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.TokenSearchParam AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.ReferenceTokenCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ BaseUri1,
+ ReferenceResourceTypeId1,
+ ReferenceResourceId1,
+ ReferenceResourceVersion1,
+ SystemId2,
+ Code2,
+ CodeOverflow2
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @ReferenceTokenCompositeSearchParams) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.DateTimeSearchParam AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenTokenCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ SystemId2,
+ Code2,
+ CodeOverflow2
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @TokenTokenCompositeSearchParams) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.TokenTokenCompositeSearchParam AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenDateTimeCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ StartDateTime2,
+ EndDateTime2,
+ IsLongerThanADay2
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @TokenDateTimeCompositeSearchParams) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.TokenDateTimeCompositeSearchParam AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenQuantityCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ SingleValue2,
+ SystemId2,
+ QuantityCodeId2,
+ LowValue2,
+ HighValue2
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @TokenQuantityCompositeSearchParams) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.TokenQuantityCompositeSearchParam AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenStringCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ Text2,
+ TextOverflow2
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @TokenStringCompositeSearchParams) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.TokenStringCompositeSearchParam AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ INSERT INTO dbo.TokenNumberNumberCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ SingleValue2,
+ LowValue2,
+ HighValue2,
+ SingleValue3,
+ LowValue3,
+ HighValue3,
+ HasRange
+ FROM (SELECT TOP (@DummyTop) *
+ FROM @TokenNumberNumberCompositeSearchParams) AS A
+ WHERE EXISTS (SELECT *
+ FROM @Existing AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT *
+ FROM dbo.TokenNumberNumberCompositeSearchParam AS C
+ WHERE C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1));
+ SET @AffectedRows += @@rowcount;
+ END
+ IF @IsResourceChangeCaptureEnabled = 1
+ EXECUTE dbo.CaptureResourceIdsForChanges @Resources, @ResourcesLake;
+ IF @TransactionId IS NOT NULL
+ EXECUTE dbo.MergeResourcesCommitTransaction @TransactionId;
+ IF @InitialTranCount = 0
+ AND @@trancount > 0
+ COMMIT TRANSACTION;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @AffectedRows, @Text = @DeletedIdMap;
+END TRY
+BEGIN CATCH
+ IF @InitialTranCount = 0
+ AND @@trancount > 0
+ ROLLBACK;
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ IF error_number() IN (2601, 2627)
+ AND error_message() LIKE '%''dbo.ResourceIdIntMap''%'
+ OR error_number() = 547
+ AND error_message() LIKE '%DELETE%'
+ BEGIN
+ DELETE @ResourcesWithIds;
+ DELETE @ReferenceSearchParamsWithIds;
+ DELETE @CurrentRefIdsRaw;
+ DELETE @CurrentRefIds;
+ DELETE @InputIds;
+ DELETE @InsertIds;
+ DELETE @InsertedIdsReference;
+ DELETE @ExistingIdsReference;
+ DELETE @InsertedIdsResource;
+ DELETE @ExistingIdsResource;
+ DELETE @Existing;
+ DELETE @ResourceInfos;
+ DELETE @PreviousSurrogateIds;
+ GOTO RetryResourceIdIntMapLogic;
+ END
+ ELSE
+ IF @RaiseExceptionOnConflict = 1
+ AND error_number() IN (2601, 2627)
+ AND (error_message() LIKE '%''dbo.Resource%'
+ OR error_message() LIKE '%''dbo.CurrentResources%'
+ OR error_message() LIKE '%''dbo.HistoryResources%'
+ OR error_message() LIKE '%''dbo.RawResources''%')
+ THROW 50409, 'Resource has been recently updated or added, please compare the resource content in code for any duplicate updates', 1;
+ ELSE
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.MergeResourcesAdvanceTransactionVisibility
+@AffectedRows INT=0 OUTPUT
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = object_name(@@procid), @Mode AS VARCHAR (100) = '', @st AS DATETIME = getUTCdate(), @msg AS VARCHAR (1000), @MaxTransactionId AS BIGINT, @MinTransactionId AS BIGINT, @MinNotCompletedTransactionId AS BIGINT, @CurrentTransactionId AS BIGINT;
+SET @AffectedRows = 0;
+BEGIN TRY
+ EXECUTE dbo.MergeResourcesGetTransactionVisibility @MinTransactionId OUTPUT;
+ SET @MinTransactionId += 1;
+ SET @CurrentTransactionId = (SELECT TOP 1 SurrogateIdRangeFirstValue
+ FROM dbo.Transactions
+ ORDER BY SurrogateIdRangeFirstValue DESC);
+ SET @MinNotCompletedTransactionId = isnull((SELECT TOP 1 SurrogateIdRangeFirstValue
+ FROM dbo.Transactions
+ WHERE IsCompleted = 0
+ AND SurrogateIdRangeFirstValue BETWEEN @MinTransactionId AND @CurrentTransactionId
+ ORDER BY SurrogateIdRangeFirstValue), @CurrentTransactionId + 1);
+ SET @MaxTransactionId = (SELECT TOP 1 SurrogateIdRangeFirstValue
+ FROM dbo.Transactions
+ WHERE IsCompleted = 1
+ AND SurrogateIdRangeFirstValue BETWEEN @MinTransactionId AND @CurrentTransactionId
+ AND SurrogateIdRangeFirstValue < @MinNotCompletedTransactionId
+ ORDER BY SurrogateIdRangeFirstValue DESC);
+ IF @MaxTransactionId >= @MinTransactionId
+ BEGIN
+ UPDATE A
+ SET IsVisible = 1,
+ VisibleDate = getUTCdate()
+ FROM dbo.Transactions AS A WITH (INDEX (1))
+ WHERE SurrogateIdRangeFirstValue BETWEEN @MinTransactionId AND @CurrentTransactionId
+ AND SurrogateIdRangeFirstValue <= @MaxTransactionId;
+ SET @AffectedRows += @@rowcount;
+ END
+ SET @msg = 'Min=' + CONVERT (VARCHAR, @MinTransactionId) + ' C=' + CONVERT (VARCHAR, @CurrentTransactionId) + ' MinNC=' + CONVERT (VARCHAR, @MinNotCompletedTransactionId) + ' Max=' + CONVERT (VARCHAR, @MaxTransactionId);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @AffectedRows, @Text = @msg;
+END TRY
+BEGIN CATCH
+ IF @@trancount > 0
+ ROLLBACK;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.MergeResourcesBeginTransaction
+@Count INT, @TransactionId BIGINT OUTPUT, @SequenceRangeFirstValue INT=NULL OUTPUT, @HeartbeatDate DATETIME=NULL
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'MergeResourcesBeginTransaction', @Mode AS VARCHAR (200) = 'Cnt=' + CONVERT (VARCHAR, @Count), @st AS DATETIME = getUTCdate(), @FirstValueVar AS SQL_VARIANT, @LastValueVar AS SQL_VARIANT;
+BEGIN TRY
+ SET @TransactionId = NULL;
+ IF @@trancount > 0
+ RAISERROR ('MergeResourcesBeginTransaction cannot be called inside outer transaction.', 18, 127);
+ SET @FirstValueVar = NULL;
+ WHILE @FirstValueVar IS NULL
+ BEGIN
+ EXECUTE sys.sp_sequence_get_range @sequence_name = 'dbo.ResourceSurrogateIdUniquifierSequence', @range_size = @Count, @range_first_value = @FirstValueVar OUTPUT, @range_last_value = @LastValueVar OUTPUT;
+ SET @SequenceRangeFirstValue = CONVERT (INT, @FirstValueVar);
+ IF @SequenceRangeFirstValue > CONVERT (INT, @LastValueVar)
+ SET @FirstValueVar = NULL;
+ END
+ SET @TransactionId = datediff_big(millisecond, '0001-01-01', sysUTCdatetime()) * 80000 + @SequenceRangeFirstValue;
+ INSERT INTO dbo.Transactions (SurrogateIdRangeFirstValue, SurrogateIdRangeLastValue, HeartbeatDate)
+ SELECT @TransactionId,
+ @TransactionId + @Count - 1,
+ isnull(@HeartbeatDate, getUTCdate());
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ IF @@trancount > 0
+ ROLLBACK;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.MergeResourcesCommitTransaction
+@TransactionId BIGINT, @FailureReason VARCHAR (MAX)=NULL, @OverrideIsControlledByClientCheck BIT=0
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'MergeResourcesCommitTransaction', @st AS DATETIME = getUTCdate(), @InitialTranCount AS INT = @@trancount, @IsCompletedBefore AS BIT, @Rows AS INT, @msg AS VARCHAR (1000);
+DECLARE @Mode AS VARCHAR (200) = 'TR=' + CONVERT (VARCHAR, @TransactionId) + ' OC=' + isnull(CONVERT (VARCHAR, @OverrideIsControlledByClientCheck), 'NULL');
+BEGIN TRY
+ IF @InitialTranCount = 0
+ BEGIN TRANSACTION;
+ UPDATE dbo.Transactions
+ SET IsCompleted = 1,
+ @IsCompletedBefore = IsCompleted,
+ EndDate = getUTCdate(),
+ IsSuccess = CASE WHEN @FailureReason IS NULL THEN 1 ELSE 0 END,
+ FailureReason = @FailureReason
+ WHERE SurrogateIdRangeFirstValue = @TransactionId
+ AND (IsControlledByClient = 1
+ OR @OverrideIsControlledByClientCheck = 1);
+ SET @Rows = @@rowcount;
+ IF @Rows = 0
+ BEGIN
+ SET @msg = 'Transaction [' + CONVERT (VARCHAR (20), @TransactionId) + '] is not controlled by client or does not exist.';
+ RAISERROR (@msg, 18, 127);
+ END
+ IF @IsCompletedBefore = 1
+ BEGIN
+ IF @InitialTranCount = 0
+ ROLLBACK;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @Rows, @Target = '@IsCompletedBefore', @Text = '=1';
+ RETURN;
+ END
+ IF @InitialTranCount = 0
+ COMMIT TRANSACTION;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @Rows;
+END TRY
+BEGIN CATCH
+ IF @InitialTranCount = 0
+ AND @@trancount > 0
+ ROLLBACK;
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.MergeResourcesDeleteInvisibleHistory
+@TransactionId BIGINT, @AffectedRows INT=NULL OUTPUT
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = object_name(@@procid), @Mode AS VARCHAR (100) = 'T=' + CONVERT (VARCHAR, @TransactionId), @st AS DATETIME, @Rows AS INT, @DeletedIdMap AS INT;
+SET @AffectedRows = 0;
+Retry:
+BEGIN TRY
+ DECLARE @Ids TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL);
+ BEGIN TRANSACTION;
+ SET @st = getUTCdate();
+ DELETE A
+ OUTPUT deleted.ResourceTypeId, deleted.ResourceIdInt INTO @Ids
+ FROM dbo.Resource AS A
+ WHERE HistoryTransactionId = @TransactionId;
+ SET @Rows = @@rowcount;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = 'Resource', @Action = 'Delete', @Start = @st, @Rows = @Rows;
+ SET @AffectedRows += @Rows;
+ SET @st = getUTCdate();
+ IF @Rows > 0
+ BEGIN
+ DELETE A
+ FROM @Ids AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.CurrentResources AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt);
+ SET @Rows -= @@rowcount;
+ IF @Rows > 0
+ BEGIN
+ DELETE A
+ FROM @Ids AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.ResourceReferenceSearchParams AS B
+ WHERE B.ReferenceResourceTypeId = A.ResourceTypeId
+ AND B.ReferenceResourceIdInt = A.ResourceIdInt);
+ SET @Rows -= @@rowcount;
+ IF @Rows > 0
+ BEGIN
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.ResourceIdIntMap AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt;
+ SET @DeletedIdMap = @@rowcount;
+ END
+ END
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Run', @Target = 'ResourceIdIntMap', @Action = 'Delete', @Start = @st, @Rows = @DeletedIdMap;
+ END
+ COMMIT TRANSACTION;
+ SET @st = getUTCdate();
+ UPDATE dbo.Resource
+ SET TransactionId = NULL
+ WHERE TransactionId = @TransactionId;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Target = 'Resource', @Action = 'Update', @Start = @st, @Rows = @@rowcount;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ IF @@trancount > 0
+ ROLLBACK;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ IF error_number() = 547
+ AND error_message() LIKE '%DELETE%'
+ BEGIN
+ DELETE @Ids;
+ GOTO Retry;
+ END
+ ELSE
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.MergeResourcesGetTimeoutTransactions
+@TimeoutSec INT
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = object_name(@@procid), @Mode AS VARCHAR (100) = 'T=' + CONVERT (VARCHAR, @TimeoutSec), @st AS DATETIME = getUTCdate(), @MinTransactionId AS BIGINT;
+BEGIN TRY
+ EXECUTE dbo.MergeResourcesGetTransactionVisibility @MinTransactionId OUTPUT;
+ SELECT SurrogateIdRangeFirstValue
+ FROM dbo.Transactions
+ WHERE SurrogateIdRangeFirstValue > @MinTransactionId
+ AND IsCompleted = 0
+ AND datediff(second, HeartbeatDate, getUTCdate()) > @TimeoutSec
+ ORDER BY SurrogateIdRangeFirstValue;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @@rowcount;
+END TRY
+BEGIN CATCH
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.MergeResourcesGetTransactionVisibility
+@TransactionId BIGINT OUTPUT
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = object_name(@@procid), @Mode AS VARCHAR (100) = '', @st AS DATETIME = getUTCdate();
+SET @TransactionId = isnull((SELECT TOP 1 SurrogateIdRangeFirstValue
+ FROM dbo.Transactions
+ WHERE IsVisible = 1
+ ORDER BY SurrogateIdRangeFirstValue DESC), -1);
+EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @@rowcount, @Text = @TransactionId;
+
+GO
+CREATE PROCEDURE dbo.MergeResourcesPutTransactionHeartbeat
+@TransactionId BIGINT
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'MergeResourcesPutTransactionHeartbeat', @Mode AS VARCHAR (100) = 'TR=' + CONVERT (VARCHAR, @TransactionId);
+BEGIN TRY
+ UPDATE dbo.Transactions
+ SET HeartbeatDate = getUTCdate()
+ WHERE SurrogateIdRangeFirstValue = @TransactionId
+ AND IsControlledByClient = 1;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.MergeResourcesPutTransactionInvisibleHistory
+@TransactionId BIGINT
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = object_name(@@procid), @Mode AS VARCHAR (100) = 'TR=' + CONVERT (VARCHAR, @TransactionId), @st AS DATETIME = getUTCdate();
+BEGIN TRY
+ UPDATE dbo.Transactions
+ SET InvisibleHistoryRemovedDate = getUTCdate()
+ WHERE SurrogateIdRangeFirstValue = @TransactionId
+ AND InvisibleHistoryRemovedDate IS NULL;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @@rowcount;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.PutJobCancelation
+@QueueType TINYINT, @GroupId BIGINT=NULL, @JobId BIGINT=NULL
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'PutJobCancelation', @Mode AS VARCHAR (100) = 'Q=' + isnull(CONVERT (VARCHAR, @QueueType), 'NULL') + ' G=' + isnull(CONVERT (VARCHAR, @GroupId), 'NULL') + ' J=' + isnull(CONVERT (VARCHAR, @JobId), 'NULL'), @st AS DATETIME = getUTCdate(), @Rows AS INT, @PartitionId AS TINYINT = @JobId % 16;
+BEGIN TRY
+ IF @JobId IS NULL
+ AND @GroupId IS NULL
+ RAISERROR ('@JobId = NULL and @GroupId = NULL', 18, 127);
+ IF @JobId IS NOT NULL
+ BEGIN
+ UPDATE dbo.JobQueue
+ SET Status = 4,
+ EndDate = getUTCdate(),
+ Version = datediff_big(millisecond, '0001-01-01', getUTCdate())
+ WHERE QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND JobId = @JobId
+ AND Status = 0;
+ SET @Rows = @@rowcount;
+ IF @Rows = 0
+ BEGIN
+ UPDATE dbo.JobQueue
+ SET CancelRequested = 1
+ WHERE QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND JobId = @JobId
+ AND Status = 1;
+ SET @Rows = @@rowcount;
+ END
+ END
+ ELSE
+ BEGIN
+ UPDATE dbo.JobQueue
+ SET Status = 4,
+ EndDate = getUTCdate(),
+ Version = datediff_big(millisecond, '0001-01-01', getUTCdate())
+ WHERE QueueType = @QueueType
+ AND GroupId = @GroupId
+ AND Status = 0;
+ SET @Rows = @@rowcount;
+ UPDATE dbo.JobQueue
+ SET CancelRequested = 1
+ WHERE QueueType = @QueueType
+ AND GroupId = @GroupId
+ AND Status = 1;
+ SET @Rows += @@rowcount;
+ END
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @Rows;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.PutJobHeartbeat
+@QueueType TINYINT, @JobId BIGINT, @Version BIGINT, @Data BIGINT=NULL, @CancelRequested BIT=0 OUTPUT
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'PutJobHeartbeat', @Mode AS VARCHAR (100), @st AS DATETIME = getUTCdate(), @Rows AS INT = 0, @PartitionId AS TINYINT = @JobId % 16;
+SET @Mode = 'Q=' + CONVERT (VARCHAR, @QueueType) + ' J=' + CONVERT (VARCHAR, @JobId) + ' P=' + CONVERT (VARCHAR, @PartitionId) + ' V=' + CONVERT (VARCHAR, @Version) + ' D=' + isnull(CONVERT (VARCHAR, @Data), 'NULL');
+BEGIN TRY
+ UPDATE dbo.JobQueue
+ SET @CancelRequested = CancelRequested,
+ HeartbeatDate = getUTCdate()
+ WHERE QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND JobId = @JobId
+ AND Status = 1
+ AND Version = @Version;
+ SET @Rows = @@rowcount;
+ IF @Rows = 0
+ AND NOT EXISTS (SELECT *
+ FROM dbo.JobQueue
+ WHERE QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND JobId = @JobId
+ AND Version = @Version
+ AND Status IN (2, 3, 4))
+ BEGIN
+ IF EXISTS (SELECT *
+ FROM dbo.JobQueue
+ WHERE QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND JobId = @JobId)
+ THROW 50412, 'Precondition failed', 1;
+ ELSE
+ THROW 50404, 'Job record not found', 1;
+ END
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @Rows;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.PutJobStatus
+@QueueType TINYINT, @JobId BIGINT, @Version BIGINT, @Failed BIT, @Data BIGINT, @FinalResult VARCHAR (MAX), @RequestCancellationOnFailure BIT
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'PutJobStatus', @Mode AS VARCHAR (100), @st AS DATETIME = getUTCdate(), @Rows AS INT = 0, @PartitionId AS TINYINT = @JobId % 16, @GroupId AS BIGINT;
+SET @Mode = 'Q=' + CONVERT (VARCHAR, @QueueType) + ' J=' + CONVERT (VARCHAR, @JobId) + ' P=' + CONVERT (VARCHAR, @PartitionId) + ' V=' + CONVERT (VARCHAR, @Version) + ' F=' + CONVERT (VARCHAR, @Failed) + ' R=' + isnull(@FinalResult, 'NULL');
+BEGIN TRY
+ UPDATE dbo.JobQueue
+ SET EndDate = getUTCdate(),
+ Status = CASE WHEN @Failed = 1 THEN 3 WHEN CancelRequested = 1 THEN 4 ELSE 2 END,
+ Data = @Data,
+ Result = @FinalResult,
+ @GroupId = GroupId
+ WHERE QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND JobId = @JobId
+ AND Status = 1
+ AND Version = @Version;
+ SET @Rows = @@rowcount;
+ IF @Rows = 0
+ BEGIN
+ SET @GroupId = (SELECT GroupId
+ FROM dbo.JobQueue
+ WHERE QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND JobId = @JobId
+ AND Version = @Version
+ AND Status IN (2, 3, 4));
+ IF @GroupId IS NULL
+ IF EXISTS (SELECT *
+ FROM dbo.JobQueue
+ WHERE QueueType = @QueueType
+ AND PartitionId = @PartitionId
+ AND JobId = @JobId)
+ THROW 50412, 'Precondition failed', 1;
+ ELSE
+ THROW 50404, 'Job record not found', 1;
+ END
+ IF @Failed = 1
+ AND @RequestCancellationOnFailure = 1
+ EXECUTE dbo.PutJobCancelation @QueueType = @QueueType, @GroupId = @GroupId;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @Rows;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error';
+ THROW;
+END CATCH
+
+GO
+CREATE OR ALTER PROCEDURE dbo.RemovePartitionFromResourceChanges_2
+@partitionNumberToSwitchOut INT, @partitionBoundaryToMerge DATETIME2 (7)
+AS
+BEGIN
+ TRUNCATE TABLE dbo.ResourceChangeDataStaging;
+ ALTER TABLE dbo.ResourceChangeData SWITCH PARTITION @partitionNumberToSwitchOut TO dbo.ResourceChangeDataStaging;
+ ALTER PARTITION FUNCTION PartitionFunction_ResourceChangeData_Timestamp( )
+ MERGE RANGE (@partitionBoundaryToMerge);
+ TRUNCATE TABLE dbo.ResourceChangeDataStaging;
+END
+
+GO
+CREATE PROCEDURE dbo.SwitchPartitionsIn
+@Tbl VARCHAR (100)
+WITH EXECUTE AS 'dbo'
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'SwitchPartitionsIn', @Mode AS VARCHAR (200) = 'Tbl=' + isnull(@Tbl, 'NULL'), @st AS DATETIME = getUTCdate(), @ResourceTypeId AS SMALLINT, @Rows AS BIGINT, @Txt AS VARCHAR (1000), @TblInt AS VARCHAR (100), @Ind AS VARCHAR (200), @IndId AS INT, @DataComp AS VARCHAR (100);
+DECLARE @Indexes TABLE (
+ IndId INT PRIMARY KEY,
+ name VARCHAR (200));
+DECLARE @ResourceTypes TABLE (
+ ResourceTypeId SMALLINT PRIMARY KEY);
+BEGIN TRY
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Start';
+ IF @Tbl IS NULL
+ RAISERROR ('@Tbl IS NULL', 18, 127);
+ INSERT INTO @Indexes
+ SELECT index_id,
+ name
+ FROM sys.indexes
+ WHERE object_id = object_id(@Tbl)
+ AND is_disabled = 1;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '@Indexes', @Action = 'Insert', @Rows = @@rowcount;
+ WHILE EXISTS (SELECT *
+ FROM @Indexes)
+ BEGIN
+ SELECT TOP 1 @IndId = IndId,
+ @Ind = name
+ FROM @Indexes
+ ORDER BY IndId;
+ SET @DataComp = CASE WHEN (SELECT PropertyValue
+ FROM dbo.IndexProperties
+ WHERE TableName = @Tbl
+ AND IndexName = @Ind) = 'PAGE' THEN ' PARTITION = ALL WITH (DATA_COMPRESSION = PAGE)' ELSE '' END;
+ SET @Txt = 'IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = object_id(''' + @Tbl + ''') AND name = ''' + @Ind + ''' AND is_disabled = 1) ALTER INDEX ' + @Ind + ' ON dbo.' + @Tbl + ' REBUILD' + @DataComp;
+ EXECUTE (@Txt);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @Ind, @Action = 'Rebuild', @Text = @Txt;
+ DELETE @Indexes
+ WHERE IndId = @IndId;
+ END
+ INSERT INTO @ResourceTypes
+ SELECT CONVERT (SMALLINT, substring(name, charindex('_', name) + 1, 6)) AS ResourceTypeId
+ FROM sys.objects AS O
+ WHERE name LIKE @Tbl + '[_]%'
+ AND EXISTS (SELECT *
+ FROM sysindexes
+ WHERE id = O.object_id
+ AND indid IN (0, 1)
+ AND rows > 0);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '#ResourceTypes', @Action = 'Select Into', @Rows = @@rowcount;
+ WHILE EXISTS (SELECT *
+ FROM @ResourceTypes)
+ BEGIN
+ SET @ResourceTypeId = (SELECT TOP 1 ResourceTypeId
+ FROM @ResourceTypes);
+ SET @TblInt = @Tbl + '_' + CONVERT (VARCHAR, @ResourceTypeId);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @TblInt;
+ SET @Txt = 'ALTER TABLE dbo.' + @TblInt + ' SWITCH TO dbo.' + @Tbl + ' PARTITION $partition.PartitionFunction_ResourceTypeId(' + CONVERT (VARCHAR, @ResourceTypeId) + ')';
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @Tbl, @Action = 'Switch in start', @Text = @Txt;
+ EXECUTE (@Txt);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @Tbl, @Action = 'Switch in', @Text = @Txt;
+ IF EXISTS (SELECT *
+ FROM sysindexes
+ WHERE id = object_id(@TblInt)
+ AND rows > 0)
+ BEGIN
+ SET @Txt = @TblInt + ' is not empty after switch';
+ RAISERROR (@Txt, 18, 127);
+ END
+ EXECUTE ('DROP TABLE dbo.' + @TblInt);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @TblInt, @Action = 'Drop';
+ DELETE @ResourceTypes
+ WHERE ResourceTypeId = @ResourceTypeId;
+ END
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.SwitchPartitionsInAllTables
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'SwitchPartitionsInAllTables', @Mode AS VARCHAR (200) = 'PS=PartitionScheme_ResourceTypeId', @st AS DATETIME = getUTCdate(), @Tbl AS VARCHAR (100);
+BEGIN TRY
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Start';
+ DECLARE @Tables TABLE (
+ name VARCHAR (100) PRIMARY KEY,
+ supported BIT );
+ INSERT INTO @Tables
+ EXECUTE dbo.GetPartitionedTables @IncludeNotDisabled = 1, @IncludeNotSupported = 0;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '@Tables', @Action = 'Insert', @Rows = @@rowcount;
+ WHILE EXISTS (SELECT *
+ FROM @Tables)
+ BEGIN
+ SET @Tbl = (SELECT TOP 1 name
+ FROM @Tables
+ ORDER BY name);
+ EXECUTE dbo.SwitchPartitionsIn @Tbl = @Tbl;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = 'SwitchPartitionsIn', @Action = 'Execute', @Text = @Tbl;
+ DELETE @Tables
+ WHERE name = @Tbl;
+ END
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.SwitchPartitionsOut
+@Tbl VARCHAR (100), @RebuildClustered BIT
+WITH EXECUTE AS 'dbo'
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'SwitchPartitionsOut', @Mode AS VARCHAR (200) = 'Tbl=' + isnull(@Tbl, 'NULL') + ' ND=' + isnull(CONVERT (VARCHAR, @RebuildClustered), 'NULL'), @st AS DATETIME = getUTCdate(), @ResourceTypeId AS SMALLINT, @Rows AS BIGINT, @Txt AS VARCHAR (MAX), @TblInt AS VARCHAR (100), @IndId AS INT, @Ind AS VARCHAR (200), @Name AS VARCHAR (100), @checkName AS VARCHAR (200), @definition AS VARCHAR (200);
+DECLARE @Indexes TABLE (
+ IndId INT PRIMARY KEY,
+ name VARCHAR (200),
+ IsDisabled BIT );
+DECLARE @IndexesRT TABLE (
+ IndId INT PRIMARY KEY,
+ name VARCHAR (200),
+ IsDisabled BIT );
+DECLARE @ResourceTypes TABLE (
+ ResourceTypeId SMALLINT PRIMARY KEY,
+ partition_number_roundtrip INT ,
+ partition_number INT ,
+ row_count BIGINT );
+DECLARE @Names TABLE (
+ name VARCHAR (100) PRIMARY KEY);
+DECLARE @CheckConstraints TABLE (
+ CheckName VARCHAR (200),
+ CheckDefinition VARCHAR (200));
+BEGIN TRY
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Start';
+ IF @Tbl IS NULL
+ RAISERROR ('@Tbl IS NULL', 18, 127);
+ IF @RebuildClustered IS NULL
+ RAISERROR ('@RebuildClustered IS NULL', 18, 127);
+ INSERT INTO @Indexes
+ SELECT index_id,
+ name,
+ is_disabled
+ FROM sys.indexes
+ WHERE object_id = object_id(@Tbl)
+ AND (is_disabled = 0
+ OR @RebuildClustered = 1);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '@Indexes', @Action = 'Insert', @Rows = @@rowcount;
+ INSERT INTO @ResourceTypes
+ SELECT partition_number - 1 AS ResourceTypeId,
+ $PARTITION.PartitionFunction_ResourceTypeId (partition_number - 1) AS partition_number_roundtrip,
+ partition_number,
+ row_count
+ FROM sys.dm_db_partition_stats
+ WHERE object_id = object_id(@Tbl)
+ AND index_id = 1
+ AND row_count > 0;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '@ResourceTypes', @Action = 'Insert', @Rows = @@rowcount, @Text = 'For partition switch';
+ IF EXISTS (SELECT *
+ FROM @ResourceTypes
+ WHERE partition_number_roundtrip <> partition_number)
+ RAISERROR ('Partition sanity check failed', 18, 127);
+ WHILE EXISTS (SELECT *
+ FROM @ResourceTypes)
+ BEGIN
+ SELECT TOP 1 @ResourceTypeId = ResourceTypeId,
+ @Rows = row_count
+ FROM @ResourceTypes
+ ORDER BY ResourceTypeId;
+ SET @TblInt = @Tbl + '_' + CONVERT (VARCHAR, @ResourceTypeId);
+ SET @Txt = 'Starting @ResourceTypeId=' + CONVERT (VARCHAR, @ResourceTypeId) + ' row_count=' + CONVERT (VARCHAR, @Rows);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Text = @Txt;
+ IF NOT EXISTS (SELECT *
+ FROM sysindexes
+ WHERE id = object_id(@TblInt)
+ AND rows > 0)
+ BEGIN
+ IF object_id(@TblInt) IS NOT NULL
+ BEGIN
+ EXECUTE ('DROP TABLE dbo.' + @TblInt);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @TblInt, @Action = 'Drop';
+ END
+ EXECUTE ('SELECT * INTO dbo.' + @TblInt + ' FROM dbo.' + @Tbl + ' WHERE 1 = 2');
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @TblInt, @Action = 'Select Into', @Rows = @@rowcount;
+ DELETE @CheckConstraints;
+ INSERT INTO @CheckConstraints
+ SELECT name,
+ definition
+ FROM sys.check_constraints
+ WHERE parent_object_id = object_id(@Tbl);
+ WHILE EXISTS (SELECT *
+ FROM @CheckConstraints)
+ BEGIN
+ SELECT TOP 1 @checkName = CheckName,
+ @definition = CheckDefinition
+ FROM @CheckConstraints;
+ SET @Txt = 'ALTER TABLE ' + @TblInt + ' ADD CHECK ' + @definition;
+ EXECUTE (@Txt);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @TblInt, @Action = 'ALTER', @Text = @Txt;
+ DELETE @CheckConstraints
+ WHERE CheckName = @checkName;
+ END
+ DELETE @Names;
+ INSERT INTO @Names
+ SELECT name
+ FROM sys.columns
+ WHERE object_id = object_id(@Tbl)
+ AND is_sparse = 1;
+ WHILE EXISTS (SELECT *
+ FROM @Names)
+ BEGIN
+ SET @Name = (SELECT TOP 1 name
+ FROM @Names
+ ORDER BY name);
+ SET @Txt = (SELECT 'ALTER TABLE dbo.' + @TblInt + ' ALTER COLUMN ' + @Name + ' ' + T.name + '(' + CONVERT (VARCHAR, C.precision) + ',' + CONVERT (VARCHAR, C.scale) + ') SPARSE NULL'
+ FROM sys.types AS T
+ INNER JOIN
+ sys.columns AS C
+ ON C.system_type_id = T.system_type_id
+ WHERE C.object_id = object_id(@Tbl)
+ AND C.name = @Name);
+ EXECUTE (@Txt);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @TblInt, @Action = 'ALTER', @Text = @Txt;
+ DELETE @Names
+ WHERE name = @Name;
+ END
+ END
+ INSERT INTO @IndexesRT
+ SELECT *
+ FROM @Indexes
+ WHERE IsDisabled = 0;
+ WHILE EXISTS (SELECT *
+ FROM @IndexesRT)
+ BEGIN
+ SELECT TOP 1 @IndId = IndId,
+ @Ind = name
+ FROM @IndexesRT
+ ORDER BY IndId;
+ IF NOT EXISTS (SELECT *
+ FROM sys.indexes
+ WHERE object_id = object_id(@TblInt)
+ AND name = @Ind)
+ BEGIN
+ EXECUTE dbo.GetIndexCommands @Tbl = @Tbl, @Ind = @Ind, @AddPartClause = 0, @IncludeClustered = 1, @Txt = @Txt OUTPUT;
+ SET @Txt = replace(@Txt, '[' + @Tbl + ']', @TblInt);
+ EXECUTE (@Txt);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @TblInt, @Action = 'Create Index', @Text = @Txt;
+ END
+ DELETE @IndexesRT
+ WHERE IndId = @IndId;
+ END
+ SET @Txt = 'ALTER TABLE dbo.' + @TblInt + ' ADD CHECK (ResourceTypeId >= ' + CONVERT (VARCHAR, @ResourceTypeId) + ' AND ResourceTypeId < ' + CONVERT (VARCHAR, @ResourceTypeId) + ' + 1)';
+ EXECUTE (@Txt);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @Tbl, @Action = 'Add check', @Text = @Txt;
+ SET @Txt = 'ALTER TABLE dbo.' + @Tbl + ' SWITCH PARTITION $partition.PartitionFunction_ResourceTypeId(' + CONVERT (VARCHAR, @ResourceTypeId) + ') TO dbo.' + @TblInt;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @Tbl, @Action = 'Switch out start', @Text = @Txt;
+ EXECUTE (@Txt);
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = @Tbl, @Action = 'Switch out end', @Text = @Txt;
+ DELETE @ResourceTypes
+ WHERE ResourceTypeId = @ResourceTypeId;
+ END
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.SwitchPartitionsOutAllTables
+@RebuildClustered BIT
+AS
+SET NOCOUNT ON;
+DECLARE @SP AS VARCHAR (100) = 'SwitchPartitionsOutAllTables', @Mode AS VARCHAR (200) = 'PS=PartitionScheme_ResourceTypeId ND=' + isnull(CONVERT (VARCHAR, @RebuildClustered), 'NULL'), @st AS DATETIME = getUTCdate(), @Tbl AS VARCHAR (100);
+BEGIN TRY
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Start';
+ DECLARE @Tables TABLE (
+ name VARCHAR (100) PRIMARY KEY,
+ supported BIT );
+ INSERT INTO @Tables
+ EXECUTE dbo.GetPartitionedTables @IncludeNotDisabled = @RebuildClustered, @IncludeNotSupported = 0;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = '@Tables', @Action = 'Insert', @Rows = @@rowcount;
+ WHILE EXISTS (SELECT *
+ FROM @Tables)
+ BEGIN
+ SET @Tbl = (SELECT TOP 1 name
+ FROM @Tables
+ ORDER BY name);
+ EXECUTE dbo.SwitchPartitionsOut @Tbl = @Tbl, @RebuildClustered = @RebuildClustered;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Info', @Target = 'SwitchPartitionsOut', @Action = 'Execute', @Text = @Tbl;
+ DELETE @Tables
+ WHERE name = @Tbl;
+ END
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st;
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750
+ THROW;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ THROW;
+END CATCH
+
+GO
+GO
+CREATE OR ALTER PROCEDURE dbo.UpdateEventAgentCheckpoint
+@CheckpointId VARCHAR (64), @LastProcessedDateTime DATETIMEOFFSET (7)=NULL, @LastProcessedIdentifier VARCHAR (64)=NULL
+AS
+BEGIN
+ IF EXISTS (SELECT *
+ FROM dbo.EventAgentCheckpoint
+ WHERE CheckpointId = @CheckpointId)
+ UPDATE dbo.EventAgentCheckpoint
+ SET CheckpointId = @CheckpointId,
+ LastProcessedDateTime = @LastProcessedDateTime,
+ LastProcessedIdentifier = @LastProcessedIdentifier,
+ UpdatedOn = sysutcdatetime()
+ WHERE CheckpointId = @CheckpointId;
+ ELSE
+ INSERT INTO dbo.EventAgentCheckpoint (CheckpointId, LastProcessedDateTime, LastProcessedIdentifier, UpdatedOn)
+ VALUES (@CheckpointId, @LastProcessedDateTime, @LastProcessedIdentifier, sysutcdatetime());
+END
+
+GO
+CREATE PROCEDURE dbo.UpdateReindexJob
+@id VARCHAR (64), @status VARCHAR (10), @rawJobRecord VARCHAR (MAX), @jobVersion BINARY (8)
+AS
+SET NOCOUNT ON;
+SET XACT_ABORT ON;
+BEGIN TRANSACTION;
+DECLARE @currentJobVersion AS BINARY (8);
+SELECT @currentJobVersion = JobVersion
+FROM dbo.ReindexJob WITH (UPDLOCK, HOLDLOCK)
+WHERE Id = @id;
+IF (@currentJobVersion IS NULL)
+ BEGIN
+ THROW 50404, 'Reindex job record not found', 1;
+ END
+IF (@jobVersion <> @currentJobVersion)
+ BEGIN
+ THROW 50412, 'Precondition failed', 1;
+ END
+DECLARE @heartbeatDateTime AS DATETIME2 (7) = SYSUTCDATETIME();
+UPDATE dbo.ReindexJob
+SET Status = @status,
+ HeartbeatDateTime = @heartbeatDateTime,
+ RawJobRecord = @rawJobRecord
+WHERE Id = @id;
+SELECT @@DBTS;
+COMMIT TRANSACTION;
+
+GO
+CREATE PROCEDURE dbo.UpdateResourceSearchParams
+@FailedResources INT=0 OUTPUT, @Resources dbo.ResourceList READONLY, @ResourcesLake dbo.ResourceListLake READONLY, @ResourceWriteClaims dbo.ResourceWriteClaimList READONLY, @ReferenceSearchParams dbo.ReferenceSearchParamList READONLY, @TokenSearchParams dbo.TokenSearchParamList READONLY, @TokenTexts dbo.TokenTextList READONLY, @StringSearchParams dbo.StringSearchParamList READONLY, @UriSearchParams dbo.UriSearchParamList READONLY, @NumberSearchParams dbo.NumberSearchParamList READONLY, @QuantitySearchParams dbo.QuantitySearchParamList READONLY, @DateTimeSearchParams dbo.DateTimeSearchParamList READONLY, @ReferenceTokenCompositeSearchParams dbo.ReferenceTokenCompositeSearchParamList READONLY, @TokenTokenCompositeSearchParams dbo.TokenTokenCompositeSearchParamList READONLY, @TokenDateTimeCompositeSearchParams dbo.TokenDateTimeCompositeSearchParamList READONLY, @TokenQuantityCompositeSearchParams dbo.TokenQuantityCompositeSearchParamList READONLY, @TokenStringCompositeSearchParams dbo.TokenStringCompositeSearchParamList READONLY, @TokenNumberNumberCompositeSearchParams dbo.TokenNumberNumberCompositeSearchParamList READONLY
+AS
+SET NOCOUNT ON;
+DECLARE @st AS DATETIME = getUTCdate(), @SP AS VARCHAR (100) = object_name(@@procid), @Mode AS VARCHAR (200) = isnull((SELECT 'RT=[' + CONVERT (VARCHAR, min(ResourceTypeId)) + ',' + CONVERT (VARCHAR, max(ResourceTypeId)) + '] Sur=[' + CONVERT (VARCHAR, min(ResourceSurrogateId)) + ',' + CONVERT (VARCHAR, max(ResourceSurrogateId)) + '] V=' + CONVERT (VARCHAR, max(Version)) + ' Rows=' + CONVERT (VARCHAR, count(*))
+ FROM (SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ Version
+ FROM @ResourcesLake
+ UNION ALL
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ Version
+ FROM @Resources) AS A), 'Input=Empty'), @ResourceRows AS INT, @InsertRows AS INT, @DeletedIdMap AS INT, @FirstIdInt AS BIGINT, @CurrentRows AS INT;
+RetryResourceIdIntMapLogic:
+BEGIN TRY
+ DECLARE @Ids TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL);
+ DECLARE @CurrentRefIdsRaw TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL);
+ DECLARE @CurrentRefIds TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL PRIMARY KEY (ResourceTypeId, ResourceIdInt));
+ DECLARE @InputRefIds AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId));
+ DECLARE @ExistingRefIds AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId));
+ DECLARE @InsertRefIds AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ IdIndex INT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId));
+ DECLARE @InsertedRefIds AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceIdInt BIGINT NOT NULL,
+ ResourceId VARCHAR (64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId));
+ DECLARE @ReferenceSearchParamsWithIds AS TABLE (
+ ResourceTypeId SMALLINT NOT NULL,
+ ResourceSurrogateId BIGINT NOT NULL,
+ SearchParamId SMALLINT NOT NULL,
+ BaseUri VARCHAR (128) COLLATE Latin1_General_100_CS_AS NULL,
+ ReferenceResourceTypeId SMALLINT NULL,
+ ReferenceResourceIdInt BIGINT NOT NULL,
+ ReferenceResourceVersion INT NULL UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt));
+ INSERT INTO @InputRefIds
+ SELECT DISTINCT ReferenceResourceTypeId,
+ ReferenceResourceId
+ FROM @ReferenceSearchParams
+ WHERE ReferenceResourceTypeId IS NOT NULL;
+ INSERT INTO @ExistingRefIds (ResourceTypeId, ResourceIdInt, ResourceId)
+ SELECT A.ResourceTypeId,
+ ResourceIdInt,
+ A.ResourceId
+ FROM @InputRefIds AS A
+ INNER JOIN
+ dbo.ResourceIdIntMap AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceId = A.ResourceId;
+ INSERT INTO @InsertRefIds (ResourceTypeId, IdIndex, ResourceId)
+ SELECT ResourceTypeId,
+ row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1,
+ ResourceId
+ FROM @InputRefIds AS A
+ WHERE NOT EXISTS (SELECT *
+ FROM @ExistingRefIds AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceId = A.ResourceId);
+ SET @InsertRows = (SELECT count(*)
+ FROM @InsertRefIds);
+ IF @InsertRows > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @InsertRows, @FirstIdInt OUTPUT;
+ INSERT INTO @InsertedRefIds (ResourceTypeId, ResourceIdInt, ResourceId)
+ SELECT ResourceTypeId,
+ IdIndex + @FirstIdInt,
+ ResourceId
+ FROM @InsertRefIds;
+ END
+ INSERT INTO @ReferenceSearchParamsWithIds (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt, ReferenceResourceVersion)
+ SELECT A.ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ BaseUri,
+ ReferenceResourceTypeId,
+ isnull(C.ResourceIdInt, B.ResourceIdInt),
+ ReferenceResourceVersion
+ FROM @ReferenceSearchParams AS A
+ LEFT OUTER JOIN
+ @InsertedRefIds AS B
+ ON B.ResourceTypeId = A.ReferenceResourceTypeId
+ AND B.ResourceId = A.ReferenceResourceId
+ LEFT OUTER JOIN
+ @ExistingRefIds AS C
+ ON C.ResourceTypeId = A.ReferenceResourceTypeId
+ AND C.ResourceId = A.ReferenceResourceId;
+ BEGIN TRANSACTION;
+ IF EXISTS (SELECT *
+ FROM @ResourcesLake)
+ UPDATE B
+ SET SearchParamHash = (SELECT SearchParamHash
+ FROM @ResourcesLake AS A
+ WHERE A.ResourceTypeId = B.ResourceTypeId
+ AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ OUTPUT deleted.ResourceTypeId, deleted.ResourceSurrogateId INTO @Ids
+ FROM dbo.Resource AS B
+ WHERE EXISTS (SELECT *
+ FROM @ResourcesLake AS A
+ WHERE A.ResourceTypeId = B.ResourceTypeId
+ AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ AND B.IsHistory = 0;
+ ELSE
+ UPDATE B
+ SET SearchParamHash = (SELECT SearchParamHash
+ FROM @Resources AS A
+ WHERE A.ResourceTypeId = B.ResourceTypeId
+ AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ OUTPUT deleted.ResourceTypeId, deleted.ResourceSurrogateId INTO @Ids
+ FROM dbo.Resource AS B
+ WHERE EXISTS (SELECT *
+ FROM @Resources AS A
+ WHERE A.ResourceTypeId = B.ResourceTypeId
+ AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ AND B.IsHistory = 0;
+ SET @ResourceRows = @@rowcount;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.ResourceWriteClaim AS B
+ ON B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ OUTPUT deleted.ReferenceResourceTypeId, deleted.ReferenceResourceIdInt INTO @CurrentRefIdsRaw
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.ResourceReferenceSearchParams AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.StringReferenceSearchParams AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenSearchParam AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenText AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.StringSearchParam AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.UriSearchParam AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.NumberSearchParam AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.QuantitySearchParam AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.DateTimeSearchParam AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.ReferenceTokenCompositeSearchParam AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenTokenCompositeSearchParam AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenDateTimeCompositeSearchParam AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenQuantityCompositeSearchParam AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenStringCompositeSearchParam AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ DELETE B
+ FROM @Ids AS A
+ INNER LOOP JOIN
+ dbo.TokenNumberNumberCompositeSearchParam AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ INSERT INTO dbo.ResourceWriteClaim (ResourceSurrogateId, ClaimTypeId, ClaimValue)
+ SELECT ResourceSurrogateId,
+ ClaimTypeId,
+ ClaimValue
+ FROM @ResourceWriteClaims;
+ INSERT INTO @CurrentRefIds
+ SELECT DISTINCT ResourceTypeId,
+ ResourceIdInt
+ FROM @CurrentRefIdsRaw;
+ SET @CurrentRows = @@rowcount;
+ IF @CurrentRows > 0
+ BEGIN
+ DELETE A
+ FROM @CurrentRefIds AS A
+ WHERE EXISTS (SELECT *
+ FROM @ReferenceSearchParamsWithIds AS B
+ WHERE B.ReferenceResourceTypeId = A.ResourceTypeId
+ AND B.ReferenceResourceIdInt = A.ResourceIdInt);
+ SET @CurrentRows -= @@rowcount;
+ IF @CurrentRows > 0
+ BEGIN
+ DELETE A
+ FROM @CurrentRefIds AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.CurrentResources AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt);
+ SET @CurrentRows -= @@rowcount;
+ IF @CurrentRows > 0
+ BEGIN
+ DELETE A
+ FROM @CurrentRefIds AS A
+ WHERE EXISTS (SELECT *
+ FROM dbo.ResourceReferenceSearchParams AS B
+ WHERE B.ReferenceResourceTypeId = A.ResourceTypeId
+ AND B.ReferenceResourceIdInt = A.ResourceIdInt);
+ SET @CurrentRows -= @@rowcount;
+ IF @CurrentRows > 0
+ BEGIN
+ DELETE B
+ FROM @CurrentRefIds AS A
+ INNER LOOP JOIN
+ dbo.ResourceIdIntMap AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceIdInt = A.ResourceIdInt;
+ SET @DeletedIdMap = @@rowcount;
+ END
+ END
+ END
+ END
+ INSERT INTO dbo.ResourceIdIntMap (ResourceTypeId, ResourceIdInt, ResourceId)
+ SELECT ResourceTypeId,
+ ResourceIdInt,
+ ResourceId
+ FROM @InsertedRefIds;
+ INSERT INTO dbo.ResourceReferenceSearchParams (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ BaseUri,
+ ReferenceResourceTypeId,
+ ReferenceResourceIdInt
+ FROM @ReferenceSearchParamsWithIds;
+ INSERT INTO dbo.StringReferenceSearchParams (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ BaseUri,
+ ReferenceResourceId
+ FROM @ReferenceSearchParams
+ WHERE ReferenceResourceTypeId IS NULL;
+ INSERT INTO dbo.TokenSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId,
+ Code,
+ CodeOverflow
+ FROM @TokenSearchParams;
+ INSERT INTO dbo.TokenText (ResourceTypeId, ResourceSurrogateId, SearchParamId, Text)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ Text
+ FROM @TokenTexts;
+ INSERT INTO dbo.StringSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, Text, TextOverflow, IsMin, IsMax)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ Text,
+ TextOverflow,
+ IsMin,
+ IsMax
+ FROM @StringSearchParams;
+ INSERT INTO dbo.UriSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, Uri)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ Uri
+ FROM @UriSearchParams;
+ INSERT INTO dbo.NumberSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SingleValue, LowValue, HighValue)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SingleValue,
+ LowValue,
+ HighValue
+ FROM @NumberSearchParams;
+ INSERT INTO dbo.QuantitySearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, QuantityCodeId, SingleValue, LowValue, HighValue)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId,
+ QuantityCodeId,
+ SingleValue,
+ LowValue,
+ HighValue
+ FROM @QuantitySearchParams;
+ INSERT INTO dbo.DateTimeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, StartDateTime, EndDateTime, IsLongerThanADay, IsMin, IsMax)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ StartDateTime,
+ EndDateTime,
+ IsLongerThanADay,
+ IsMin,
+ IsMax
+ FROM @DateTimeSearchParams;
+ INSERT INTO dbo.ReferenceTokenCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri1, ReferenceResourceTypeId1, ReferenceResourceId1, ReferenceResourceVersion1, SystemId2, Code2, CodeOverflow2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ BaseUri1,
+ ReferenceResourceTypeId1,
+ ReferenceResourceId1,
+ ReferenceResourceVersion1,
+ SystemId2,
+ Code2,
+ CodeOverflow2
+ FROM @ReferenceTokenCompositeSearchParams;
+ INSERT INTO dbo.TokenTokenCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SystemId2, Code2, CodeOverflow2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ SystemId2,
+ Code2,
+ CodeOverflow2
+ FROM @TokenTokenCompositeSearchParams;
+ INSERT INTO dbo.TokenDateTimeCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, StartDateTime2, EndDateTime2, IsLongerThanADay2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ StartDateTime2,
+ EndDateTime2,
+ IsLongerThanADay2
+ FROM @TokenDateTimeCompositeSearchParams;
+ INSERT INTO dbo.TokenQuantityCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, SystemId2, QuantityCodeId2, LowValue2, HighValue2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ SingleValue2,
+ SystemId2,
+ QuantityCodeId2,
+ LowValue2,
+ HighValue2
+ FROM @TokenQuantityCompositeSearchParams;
+ INSERT INTO dbo.TokenStringCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, Text2, TextOverflow2)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ Text2,
+ TextOverflow2
+ FROM @TokenStringCompositeSearchParams;
+ INSERT INTO dbo.TokenNumberNumberCompositeSearchParam (ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId1, Code1, CodeOverflow1, SingleValue2, LowValue2, HighValue2, SingleValue3, LowValue3, HighValue3, HasRange)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ SystemId1,
+ Code1,
+ CodeOverflow1,
+ SingleValue2,
+ LowValue2,
+ HighValue2,
+ SingleValue3,
+ LowValue3,
+ HighValue3,
+ HasRange
+ FROM @TokenNumberNumberCompositeSearchParams;
+ COMMIT TRANSACTION;
+ SET @FailedResources = (SELECT count(*)
+ FROM @Resources) + (SELECT count(*)
+ FROM @ResourcesLake) - @ResourceRows;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'End', @Start = @st, @Rows = @ResourceRows, @Text = @DeletedIdMap;
+END TRY
+BEGIN CATCH
+ IF @@trancount > 0
+ ROLLBACK;
+ EXECUTE dbo.LogEvent @Process = @SP, @Mode = @Mode, @Status = 'Error', @Start = @st;
+ IF error_number() IN (2601, 2627)
+ AND error_message() LIKE '%''dbo.ResourceIdIntMap''%'
+ OR error_number() = 547
+ AND error_message() LIKE '%DELETE%'
+ BEGIN
+ DELETE @Ids;
+ DELETE @InputRefIds;
+ DELETE @CurrentRefIdsRaw;
+ DELETE @CurrentRefIds;
+ DELETE @ExistingRefIds;
+ DELETE @InsertRefIds;
+ DELETE @InsertedRefIds;
+ DELETE @ReferenceSearchParamsWithIds;
+ GOTO RetryResourceIdIntMapLogic;
+ END
+ ELSE
+ THROW;
+END CATCH
+
+GO
+CREATE PROCEDURE dbo.UpsertSearchParams
+@searchParams dbo.SearchParamTableType_2 READONLY
+AS
+SET NOCOUNT ON;
+SET XACT_ABORT ON;
+SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+BEGIN TRANSACTION;
+DECLARE @lastUpdated AS DATETIMEOFFSET (7) = SYSDATETIMEOFFSET();
+DECLARE @summaryOfChanges TABLE (
+ Uri VARCHAR (128) COLLATE Latin1_General_100_CS_AS NOT NULL,
+ Action VARCHAR (20) NOT NULL);
+MERGE INTO dbo.SearchParam WITH (TABLOCKX)
+ AS target
+USING @searchParams AS source ON target.Uri = source.Uri
+WHEN MATCHED THEN UPDATE
+SET Status = source.Status,
+ LastUpdated = @lastUpdated,
+ IsPartiallySupported = source.IsPartiallySupported
+WHEN NOT MATCHED BY TARGET THEN INSERT (Uri, Status, LastUpdated, IsPartiallySupported) VALUES (source.Uri, source.Status, @lastUpdated, source.IsPartiallySupported)
+OUTPUT source.Uri, $ACTION INTO @summaryOfChanges;
+SELECT SearchParamId,
+ SearchParam.Uri
+FROM dbo.SearchParam AS searchParam
+ INNER JOIN
+ @summaryOfChanges AS upsertedSearchParam
+ ON searchParam.Uri = upsertedSearchParam.Uri
+WHERE upsertedSearchParam.Action = 'INSERT';
+COMMIT TRANSACTION;
+
+GO
+CREATE VIEW dbo.CurrentResource
+AS
+SELECT A.ResourceTypeId,
+ A.ResourceSurrogateId,
+ ResourceId,
+ A.ResourceIdInt,
+ Version,
+ IsHistory,
+ IsDeleted,
+ RequestMethod,
+ RawResource,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ TransactionId,
+ HistoryTransactionId,
+ FileId,
+ OffsetInFile
+FROM dbo.CurrentResources AS A
+ LEFT OUTER JOIN
+ dbo.RawResources AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ LEFT OUTER JOIN
+ dbo.ResourceIdIntMap AS C
+ ON C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceIdInt = A.ResourceIdInt;
+
+GO
+CREATE VIEW dbo.ReferenceSearchParam
+AS
+SELECT A.ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ BaseUri,
+ ReferenceResourceTypeId,
+ B.ResourceId AS ReferenceResourceId,
+ ReferenceResourceIdInt,
+ IsResourceRef
+FROM dbo.ResourceReferenceSearchParams AS A
+ LEFT OUTER JOIN
+ dbo.ResourceIdIntMap AS B
+ ON B.ResourceTypeId = A.ReferenceResourceTypeId
+ AND B.ResourceIdInt = A.ReferenceResourceIdInt
+UNION ALL
+SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ SearchParamId,
+ BaseUri,
+ NULL,
+ ReferenceResourceId,
+ NULL,
+ IsResourceRef
+FROM dbo.StringReferenceSearchParams;
+
+GO
+CREATE VIEW dbo.Resource
+AS
+SELECT A.ResourceTypeId,
+ A.ResourceSurrogateId,
+ ResourceId,
+ A.ResourceIdInt,
+ Version,
+ IsHistory,
+ IsDeleted,
+ RequestMethod,
+ RawResource,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ TransactionId,
+ HistoryTransactionId,
+ FileId,
+ OffsetInFile
+FROM dbo.CurrentResources AS A
+ LEFT OUTER JOIN
+ dbo.RawResources AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ LEFT OUTER JOIN
+ dbo.ResourceIdIntMap AS C
+ ON C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceIdInt = A.ResourceIdInt
+UNION ALL
+SELECT A.ResourceTypeId,
+ A.ResourceSurrogateId,
+ ResourceId,
+ A.ResourceIdInt,
+ Version,
+ IsHistory,
+ IsDeleted,
+ RequestMethod,
+ RawResource,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ TransactionId,
+ HistoryTransactionId,
+ FileId,
+ OffsetInFile
+FROM dbo.HistoryResources AS A
+ LEFT OUTER JOIN
+ dbo.RawResources AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ LEFT OUTER JOIN
+ dbo.ResourceIdIntMap AS C
+ ON C.ResourceTypeId = A.ResourceTypeId
+ AND C.ResourceIdInt = A.ResourceIdInt;
+
+
+GO
+CREATE TRIGGER dbo.ResourceIns
+ ON dbo.Resource
+ INSTEAD OF INSERT
+ AS BEGIN
+ INSERT INTO dbo.RawResources (ResourceTypeId, ResourceSurrogateId, RawResource)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ RawResource
+ FROM Inserted
+ WHERE RawResource IS NOT NULL;
+ INSERT INTO dbo.CurrentResources (ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ ResourceIdInt,
+ Version,
+ IsDeleted,
+ RequestMethod,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ TransactionId,
+ HistoryTransactionId,
+ FileId,
+ OffsetInFile
+ FROM Inserted
+ WHERE IsHistory = 0;
+ INSERT INTO dbo.HistoryResources (ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ ResourceIdInt,
+ Version,
+ IsDeleted,
+ RequestMethod,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ TransactionId,
+ HistoryTransactionId,
+ FileId,
+ OffsetInFile
+ FROM Inserted
+ WHERE IsHistory = 1;
+ END
+
+
+GO
+CREATE TRIGGER dbo.ResourceUpd
+ ON dbo.Resource
+ INSTEAD OF UPDATE
+ AS BEGIN
+ IF UPDATE (IsDeleted)
+ AND UPDATE (RawResource)
+ AND UPDATE (SearchParamHash)
+ AND UPDATE (HistoryTransactionId)
+ AND NOT UPDATE (IsHistory)
+ BEGIN
+ UPDATE B
+ SET RawResource = A.RawResource
+ FROM Inserted AS A
+ INNER JOIN
+ dbo.RawResources AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ IF @@rowcount = 0
+ INSERT INTO dbo.RawResources (ResourceTypeId, ResourceSurrogateId, RawResource)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ RawResource
+ FROM Inserted
+ WHERE RawResource IS NOT NULL;
+ UPDATE B
+ SET IsDeleted = A.IsDeleted,
+ SearchParamHash = A.SearchParamHash,
+ HistoryTransactionId = A.HistoryTransactionId
+ FROM Inserted AS A
+ INNER JOIN
+ dbo.CurrentResources AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ RETURN;
+ END
+ IF UPDATE (SearchParamHash)
+ AND NOT UPDATE (IsHistory)
+ BEGIN
+ UPDATE B
+ SET SearchParamHash = A.SearchParamHash
+ FROM Inserted AS A
+ INNER JOIN
+ dbo.CurrentResources AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ WHERE A.IsHistory = 0;
+ RETURN;
+ END
+ IF UPDATE (TransactionId)
+ AND NOT UPDATE (IsHistory)
+ BEGIN
+ UPDATE B
+ SET TransactionId = A.TransactionId
+ FROM Inserted AS A
+ INNER JOIN
+ dbo.CurrentResources AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ AND B.IsHistory = 0;
+ UPDATE B
+ SET TransactionId = A.TransactionId
+ FROM Inserted AS A
+ INNER JOIN
+ dbo.HistoryResources AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ AND B.IsHistory = 1;
+ RETURN;
+ END
+ IF UPDATE (RawResource)
+ BEGIN
+ UPDATE B
+ SET RawResource = A.RawResource
+ FROM Inserted AS A
+ INNER JOIN
+ dbo.RawResources AS B
+ ON B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId;
+ IF @@rowcount = 0
+ INSERT INTO dbo.RawResources (ResourceTypeId, ResourceSurrogateId, RawResource)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ RawResource
+ FROM Inserted
+ WHERE RawResource IS NOT NULL;
+ END
+ IF NOT UPDATE (IsHistory)
+ RAISERROR ('Generic updates are not supported via Resource view', 18, 127);
+ DELETE A
+ FROM dbo.CurrentResources AS A
+ WHERE EXISTS (SELECT *
+ FROM Inserted AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ AND B.IsHistory = 1);
+ INSERT INTO dbo.HistoryResources (ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile)
+ SELECT ResourceTypeId,
+ ResourceSurrogateId,
+ ResourceIdInt,
+ Version,
+ IsDeleted,
+ RequestMethod,
+ IsRawResourceMetaSet,
+ SearchParamHash,
+ TransactionId,
+ HistoryTransactionId,
+ FileId,
+ OffsetInFile
+ FROM Inserted
+ WHERE IsHistory = 1;
+ END
+
+
+GO
+CREATE TRIGGER dbo.ResourceDel
+ ON dbo.Resource
+ INSTEAD OF DELETE
+ AS BEGIN
+ DELETE A
+ FROM dbo.CurrentResources AS A
+ WHERE EXISTS (SELECT *
+ FROM Deleted AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ AND B.IsHistory = 0);
+ DELETE A
+ FROM dbo.HistoryResources AS A
+ WHERE EXISTS (SELECT *
+ FROM Deleted AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ AND B.IsHistory = 1);
+ DELETE A
+ FROM dbo.RawResources AS A
+ WHERE EXISTS (SELECT *
+ FROM Deleted AS B
+ WHERE B.ResourceTypeId = A.ResourceTypeId
+ AND B.ResourceSurrogateId = A.ResourceSurrogateId);
+ END
+
+GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/SchemaVersion.cs b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/SchemaVersion.cs
index e633808e9d..c66ff4c131 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/SchemaVersion.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/SchemaVersion.cs
@@ -94,5 +94,6 @@ public enum SchemaVersion
V82 = 82,
V83 = 83,
V84 = 84,
+ V85 = 85,
}
}
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/SchemaVersionConstants.cs b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/SchemaVersionConstants.cs
index 92d8be3e8a..2536f83a9d 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/SchemaVersionConstants.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/SchemaVersionConstants.cs
@@ -7,8 +7,8 @@ namespace Microsoft.Health.Fhir.SqlServer.Features.Schema
{
public static class SchemaVersionConstants
{
- public const int Min = (int)SchemaVersion.V83;
- public const int Max = (int)SchemaVersion.V84;
+ public const int Min = (int)SchemaVersion.V84;
+ public const int Max = (int)SchemaVersion.V85;
public const int MinForUpgrade = (int)SchemaVersion.V83; // this is used for upgrade tests only
public const int SearchParameterStatusSchemaVersion = (int)SchemaVersion.V6;
public const int SupportForReferencesWithMissingTypeVersion = (int)SchemaVersion.V7;
@@ -33,6 +33,7 @@ public static class SchemaVersionConstants
public const int Merge = (int)SchemaVersion.V50;
public const int IncrementalImport = (int)SchemaVersion.V53;
public const int LakePrerequisite = (int)SchemaVersion.V84;
+ public const int Lake = (int)SchemaVersion.V85;
// It is currently used in Azure Healthcare APIs.
public const int ParameterizedRemovePartitionFromResourceChangesVersion = (int)SchemaVersion.V21;
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Scripts/Sequences.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Scripts/Sequences.sql
index ffa8769217..e8e0f7c909 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Scripts/Sequences.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Scripts/Sequences.sql
@@ -13,3 +13,12 @@ CREATE SEQUENCE dbo.ResourceSurrogateIdUniquifierSequence
CYCLE
CACHE 1000000
GO
+CREATE SEQUENCE dbo.ResourceIdIntMapSequence
+ AS int
+ START WITH 0
+ INCREMENT BY 1
+ MINVALUE 0
+ MAXVALUE 79999
+ CYCLE
+ CACHE 1000000
+GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Scripts/TransactionCheckWithInitialiScript.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Scripts/TransactionCheckWithInitialiScript.sql
index 4e8bc838ee..b3634a6b06 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Scripts/TransactionCheckWithInitialiScript.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Scripts/TransactionCheckWithInitialiScript.sql
@@ -19,6 +19,6 @@ Go
INSERT INTO dbo.SchemaVersion
VALUES
- (84, 'started')
+ (85, 'started')
Go
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/AssignResourceIdInts.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/AssignResourceIdInts.sql
new file mode 100644
index 0000000000..8beb517310
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/AssignResourceIdInts.sql
@@ -0,0 +1,29 @@
+CREATE PROCEDURE dbo.AssignResourceIdInts @Count int, @FirstIdInt bigint OUT
+AS
+set nocount on
+DECLARE @SP varchar(100) = 'AssignResourceIdInts'
+ ,@Mode varchar(200) = 'Cnt='+convert(varchar,@Count)
+ ,@st datetime = getUTCdate()
+ ,@FirstValueVar sql_variant
+ ,@LastValueVar sql_variant
+ ,@SequenceRangeFirstValue int
+
+BEGIN TRY
+ SET @FirstValueVar = NULL
+ WHILE @FirstValueVar IS NULL
+ BEGIN
+ EXECUTE sys.sp_sequence_get_range @sequence_name = 'dbo.ResourceIdIntMapSequence', @range_size = @Count, @range_first_value = @FirstValueVar OUT, @range_last_value = @LastValueVar OUT
+ SET @SequenceRangeFirstValue = convert(int,@FirstValueVar)
+ IF @SequenceRangeFirstValue > convert(int,@LastValueVar)
+ SET @FirstValueVar = NULL
+ END
+
+ SET @FirstIdInt = datediff_big(millisecond,'0001-01-01',sysUTCdatetime()) * 80000 + @SequenceRangeFirstValue
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+ IF @@trancount > 0 ROLLBACK TRANSACTION
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Error';
+ THROW
+END CATCH
+GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/CaptureResourceIdsForChanges.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/CaptureResourceIdsForChanges.sql
index 9afaf3007c..85322776a8 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/CaptureResourceIdsForChanges.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/CaptureResourceIdsForChanges.sql
@@ -1,10 +1,10 @@
-CREATE PROCEDURE dbo.CaptureResourceIdsForChanges @Resources dbo.ResourceList READONLY
+CREATE PROCEDURE dbo.CaptureResourceIdsForChanges @Resources dbo.ResourceList READONLY, @ResourcesLake dbo.ResourceListLake READONLY
AS
set nocount on
-- This procedure is intended to be called from the MergeResources procedure and relies on its transaction logic
INSERT INTO dbo.ResourceChangeData
( ResourceId, ResourceTypeId, ResourceVersion, ResourceChangeTypeId )
SELECT ResourceId, ResourceTypeId, Version, CASE WHEN IsDeleted = 1 THEN 2 WHEN Version > 1 THEN 1 ELSE 0 END
- FROM @Resources
+ FROM (SELECT ResourceId, ResourceTypeId, Version, IsHistory, IsDeleted FROM @Resources UNION ALL SELECT ResourceId, ResourceTypeId, Version, IsHistory, IsDeleted FROM @ResourcesLake) A
WHERE IsHistory = 0
GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/CleanupResourceIdIntMap.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/CleanupResourceIdIntMap.sql
new file mode 100644
index 0000000000..862d87d51e
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/CleanupResourceIdIntMap.sql
@@ -0,0 +1,109 @@
+--DROP PROCEDURE dbo.CleanupResourceIdIntMap
+GO
+CREATE PROCEDURE dbo.CleanupResourceIdIntMap @ResetAfter bit = 0
+AS
+set nocount on
+DECLARE @SP varchar(100) = 'CleanupResourceIdIntMap'
+ ,@Mode varchar(100) = 'R='+isnull(convert(varchar,@ResetAfter),'NULL')
+ ,@st datetime = getUTCdate()
+ ,@Id varchar(100) = 'CleanupResourceIdIntMap.LastProcessed.TypeId.ResourceIdInt'
+ ,@ResourceTypeId smallint
+ ,@ResourceIdInt bigint
+ ,@RowsToProcess int
+ ,@ProcessedRows int = 0
+ ,@DeletedRows int = 0
+ ,@ReportDate datetime = getUTCdate()
+DECLARE @LastProcessed varchar(100) = isnull((SELECT Char FROM dbo.Parameters WHERE Id = @Id),'0.0')
+
+BEGIN TRY
+ INSERT INTO dbo.Parameters (Id, Char) SELECT @SP, 'LogEvent'
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Start'
+
+ INSERT INTO dbo.Parameters (Id, Char) SELECT @Id, '0.0'
+
+ DECLARE @Types TABLE (ResourceTypeId smallint PRIMARY KEY, Name varchar(100))
+ DECLARE @ResourceIdInts TABLE (ResourceIdInt bigint PRIMARY KEY)
+
+ INSERT INTO @Types EXECUTE dbo.GetUsedResourceTypes
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Run',@Target='@Types',@Action='Insert',@Rows=@@rowcount
+
+ SET @ResourceTypeId = substring(@LastProcessed, 1, charindex('.', @LastProcessed) - 1) -- (SELECT value FROM string_split(@LastProcessed, '.', 1) WHERE ordinal = 1)
+ SET @ResourceIdInt = substring(@LastProcessed, charindex('.', @LastProcessed) + 1, 255) -- (SELECT value FROM string_split(@LastProcessed, '.', 1) WHERE ordinal = 2)
+
+ DELETE FROM @Types WHERE ResourceTypeId < @ResourceTypeId
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Run',@Target='@Types',@Action='Delete',@Rows=@@rowcount
+
+ WHILE EXISTS (SELECT * FROM @Types) -- Processing in ASC order
+ BEGIN
+ SET @ResourceTypeId = (SELECT TOP 1 ResourceTypeId FROM @Types ORDER BY ResourceTypeId)
+
+ SET @ProcessedRows = 0
+ SET @DeletedRows = 0
+ SET @RowsToProcess = 1
+ WHILE @RowsToProcess > 0
+ BEGIN
+ DELETE FROM @ResourceIdInts
+
+ INSERT INTO @ResourceIdInts
+ SELECT TOP 100000
+ ResourceIdInt
+ FROM dbo.ResourceIdIntMap
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceIdInt > @ResourceIdInt
+ ORDER BY
+ ResourceIdInt
+ SET @RowsToProcess = @@rowcount
+ SET @ProcessedRows += @RowsToProcess
+
+ IF @RowsToProcess > 0
+ SET @ResourceIdInt = (SELECT max(ResourceIdInt) FROM @ResourceIdInts)
+
+ SET @LastProcessed = convert(varchar,@ResourceTypeId)+'.'+convert(varchar,@ResourceIdInt)
+
+ DELETE FROM A FROM @ResourceIdInts A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = @ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Run',@Target='@ResourceIdInts.Current',@Action='Delete',@Rows=@@rowcount,@Text=@LastProcessed
+
+ DELETE FROM A FROM @ResourceIdInts A WHERE EXISTS (SELECT * FROM dbo.HistoryResources B WHERE B.ResourceTypeId = @ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Run',@Target='@ResourceIdInts.History',@Action='Delete',@Rows=@@rowcount,@Text=@LastProcessed
+
+ DELETE FROM A FROM @ResourceIdInts A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = @ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Run',@Target='@ResourceIdInts.Reference',@Action='Delete',@Rows=@@rowcount,@Text=@LastProcessed
+
+ IF EXISTS (SELECT * FROM @ResourceIdInts)
+ BEGIN
+ DELETE FROM A FROM dbo.ResourceIdIntMap A WHERE A.ResourceTypeId = @ResourceTypeId AND EXISTS (SELECT * FROM @ResourceIdInts B WHERE B.ResourceIdInt = A.ResourceIdInt)
+ SET @DeletedRows += @@rowcount
+ END
+
+ UPDATE dbo.Parameters SET Char = @LastProcessed WHERE Id = @Id
+
+ IF datediff(second, @ReportDate, getUTCdate()) > 60
+ BEGIN
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Run',@Target='ResourceIdIntMap',@Action='Select',@Rows=@ProcessedRows,@Text=@LastProcessed
+ SET @ReportDate = getUTCdate()
+ SET @ProcessedRows = 0
+ END
+ END
+
+ DELETE FROM @Types WHERE ResourceTypeId = @ResourceTypeId
+
+ SET @ResourceIdInt = 0
+ END
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Run',@Target='ResourceIdIntMap',@Action='Delete',@Rows=@DeletedRows,@Text=@LastProcessed
+
+ IF @ResetAfter = 1 DELETE FROM dbo.Parameters WHERE Id = @Id
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='End',@Start=@st
+END TRY
+BEGIN CATCH
+ IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Error';
+ THROW
+END CATCH
+GO
+--EXECUTE dbo.CleanupResourceIdIntMap 1
+--SELECT * FROM Parameters WHERE Id = 'CleanupResourceIdIntMap.LastProcessed.TypeId.ResourceIdInt'
+--SELECT TOP 100 * FROM EventLog WHERE EventDate > dateadd(minute,-10,getUTCdate()) AND Process = 'CleanupResourceIdIntMap' ORDER BY EventDate DESC
+--INSERT INTO Parameters (Id, Char) SELECT 'CleanupResourceIdIntMap','LogEvent'
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/DisableIndexes.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/DisableIndexes.sql
index 776ed1ac45..eae8002cf7 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/DisableIndexes.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/DisableIndexes.sql
@@ -39,7 +39,7 @@ BEGIN TRY
WHERE NOT EXISTS (SELECT * FROM dbo.IndexProperties WHERE TableName = Tbl AND IndexName = Ind)
EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Info',@Target='IndexProperties',@Action='Insert',@Rows=@@rowcount
- DELETE FROM @Indexes WHERE Tbl = 'Resource' OR IndId = 1
+ DELETE FROM @Indexes WHERE Tbl IN ('Resource','ResourceCurrent','ResourceHistory') OR IndId = 1
EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Info',@Target='@Indexes',@Action='Delete',@Rows=@@rowcount
WHILE EXISTS (SELECT * FROM @Indexes)
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResourceVersions.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResourceVersions.sql
index e5293388cd..3b13cbd923 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResourceVersions.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResourceVersions.sql
@@ -22,12 +22,17 @@ BEGIN TRY
END
,MatchedVersion = isnull(D.Version,0)
,MatchedRawResource = D.RawResource
+ ,MatchedFileId = D.FileId
+ ,MatchedOffsetInFile = D.OffsetInFile
-- ResourceIndex allows to deal with more than one late arrival per resource
- FROM (SELECT TOP (@DummyTop) *, ResourceIndex = convert(int,row_number() OVER (PARTITION BY ResourceTypeId, ResourceId ORDER BY ResourceSurrogateId DESC)) FROM @ResourceDateKeys) A
- OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId_Version) WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.Version > 0 AND B.ResourceSurrogateId < A.ResourceSurrogateId ORDER BY B.ResourceSurrogateId DESC) L -- lower
- OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId_Version) WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.Version > 0 AND B.ResourceSurrogateId > A.ResourceSurrogateId ORDER BY B.ResourceSurrogateId) U -- upper
- OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId_Version) WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.Version < 0 ORDER BY B.Version) M -- minus
- OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId_Version) WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.ResourceSurrogateId BETWEEN A.ResourceSurrogateId AND A.ResourceSurrogateId + 79999) D -- date
+ FROM (SELECT TOP (@DummyTop) A.*, M.ResourceIdInt, ResourceIndex = convert(int,row_number() OVER (PARTITION BY A.ResourceTypeId, A.ResourceId ORDER BY ResourceSurrogateId DESC))
+ FROM @ResourceDateKeys A
+ LEFT OUTER JOIN dbo.ResourceIdIntMap M WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON M.ResourceTypeId = A.ResourceTypeId AND M.ResourceId = A.ResourceId
+ ) A
+ OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt AND B.Version > 0 AND B.ResourceSurrogateId < A.ResourceSurrogateId ORDER BY B.ResourceSurrogateId DESC) L -- lower
+ OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt AND B.Version > 0 AND B.ResourceSurrogateId > A.ResourceSurrogateId ORDER BY B.ResourceSurrogateId) U -- upper
+ OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt AND B.Version < 0 ORDER BY B.Version) M -- minus
+ OUTER APPLY (SELECT TOP 1 * FROM dbo.Resource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt AND B.ResourceSurrogateId BETWEEN A.ResourceSurrogateId AND A.ResourceSurrogateId + 79999) D -- date
OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='End',@Start=@st,@Rows=@@rowcount
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResources.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResources.sql
index b9eb179a76..5e3ab4aa0f 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResources.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResources.sql
@@ -6,7 +6,6 @@ set nocount on
DECLARE @st datetime = getUTCdate()
,@SP varchar(100) = 'GetResources'
,@InputRows int
- ,@DummyTop bigint = 9223372036854775807
,@NotNullVersionExists bit
,@NullVersionExists bit
,@MinRT smallint
@@ -22,57 +21,69 @@ BEGIN TRY
SELECT B.ResourceTypeId
,B.ResourceId
,ResourceSurrogateId
- ,B.Version
+ ,C.Version
,IsDeleted
,IsHistory
,RawResource
,IsRawResourceMetaSet
,SearchParamHash
- FROM (SELECT TOP (@DummyTop) * FROM @ResourceKeys) A
- JOIN dbo.Resource B WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId_Version) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.Version = A.Version
- OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ ,FileId
+ ,OffsetInFile
+ FROM (SELECT * FROM @ResourceKeys) A
+ INNER LOOP JOIN dbo.ResourceIdIntMap B WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN dbo.Resource C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = B.ResourceIdInt AND C.Version = A.Version
+ OPTION (MAXDOP 1)
ELSE
SELECT *
FROM (SELECT B.ResourceTypeId
,B.ResourceId
,ResourceSurrogateId
- ,B.Version
+ ,C.Version
,IsDeleted
,IsHistory
,RawResource
,IsRawResourceMetaSet
,SearchParamHash
- FROM (SELECT TOP (@DummyTop) * FROM @ResourceKeys WHERE Version IS NOT NULL) A
- JOIN dbo.Resource B WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId_Version) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.Version = A.Version
+ ,FileId
+ ,OffsetInFile
+ FROM (SELECT * FROM @ResourceKeys WHERE Version IS NOT NULL) A
+ INNER LOOP JOIN dbo.ResourceIdIntMap B WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN dbo.Resource C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = B.ResourceIdInt AND C.Version = A.Version
UNION ALL
SELECT B.ResourceTypeId
,B.ResourceId
- ,ResourceSurrogateId
- ,B.Version
+ ,C.ResourceSurrogateId
+ ,C.Version
,IsDeleted
,IsHistory
,RawResource
,IsRawResourceMetaSet
,SearchParamHash
- FROM (SELECT TOP (@DummyTop) * FROM @ResourceKeys WHERE Version IS NULL) A
- JOIN dbo.Resource B WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
- WHERE IsHistory = 0
+ ,FileId
+ ,OffsetInFile
+ FROM (SELECT * FROM @ResourceKeys WHERE Version IS NULL) A
+ INNER LOOP JOIN dbo.ResourceIdIntMap B WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN dbo.CurrentResources C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = B.ResourceIdInt AND C.IsHistory = 0
+ LEFT OUTER JOIN dbo.RawResources D ON D.ResourceTypeId = A.ResourceTypeId AND D.ResourceSurrogateId = C.ResourceSurrogateId
) A
- OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ OPTION (MAXDOP 1)
ELSE
SELECT B.ResourceTypeId
,B.ResourceId
- ,ResourceSurrogateId
- ,B.Version
+ ,C.ResourceSurrogateId
+ ,C.Version
,IsDeleted
,IsHistory
,RawResource
,IsRawResourceMetaSet
,SearchParamHash
- FROM (SELECT TOP (@DummyTop) * FROM @ResourceKeys) A
- JOIN dbo.Resource B WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
- WHERE IsHistory = 0
- OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ ,FileId
+ ,OffsetInFile
+ FROM (SELECT * FROM @ResourceKeys) A
+ INNER LOOP JOIN dbo.ResourceIdIntMap B WITH (INDEX = U_ResourceIdIntMap_ResourceId_ResourceTypeId) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ INNER LOOP JOIN dbo.CurrentResources C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = B.ResourceIdInt
+ LEFT OUTER JOIN dbo.RawResources D ON D.ResourceTypeId = A.ResourceTypeId AND D.ResourceSurrogateId = C.ResourceSurrogateId
+ OPTION (MAXDOP 1)
EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='End',@Start=@st,@Rows=@@rowcount
END TRY
@@ -83,5 +94,5 @@ BEGIN CATCH
END CATCH
GO
--DECLARE @ResourceKeys dbo.ResourceKeyList
---INSERT INTO @ResourceKeys SELECT TOP 1 ResourceTypeId, ResourceId, NULL FROM Resource
+--INSERT INTO @ResourceKeys SELECT 96, newid(), NULL
--EXECUTE dbo.GetResources @ResourceKeys
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResourcesByTransactionId.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResourcesByTransactionId.sql
index e5e8a18c83..85d93fbd35 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResourcesByTransactionId.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResourcesByTransactionId.sql
@@ -6,23 +6,8 @@ set nocount on
DECLARE @SP varchar(100) = object_name(@@procid)
,@Mode varchar(100) = 'T='+convert(varchar,@TransactionId)+' H='+convert(varchar,@IncludeHistory)
,@st datetime = getUTCdate()
- ,@DummyTop bigint = 9223372036854775807
- ,@TypeId smallint
BEGIN TRY
- DECLARE @Types TABLE (TypeId smallint PRIMARY KEY, Name varchar(100))
- INSERT INTO @Types EXECUTE dbo.GetUsedResourceTypes
-
- DECLARE @Keys TABLE (TypeId smallint, SurrogateId bigint PRIMARY KEY (TypeId, SurrogateId))
- WHILE EXISTS (SELECT * FROM @Types)
- BEGIN
- SET @TypeId = (SELECT TOP 1 TypeId FROM @Types ORDER BY TypeId)
-
- INSERT INTO @Keys SELECT @TypeId, ResourceSurrogateId FROM dbo.Resource WHERE ResourceTypeId = @TypeId AND TransactionId = @TransactionId
-
- DELETE FROM @Types WHERE TypeId = @TypeId
- END
-
IF @ReturnResourceKeysOnly = 0
SELECT ResourceTypeId
,ResourceId
@@ -34,20 +19,20 @@ BEGIN TRY
,IsRawResourceMetaSet
,SearchParamHash
,RequestMethod
- FROM (SELECT TOP (@DummyTop) * FROM @Keys) A
- JOIN dbo.Resource B ON ResourceTypeId = TypeId AND ResourceSurrogateId = SurrogateId
- WHERE IsHistory = 0 OR @IncludeHistory = 1
- OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ ,FileId
+ ,OffsetInFile
+ FROM dbo.Resource
+ WHERE TransactionId = @TransactionId AND (IsHistory = 0 OR @IncludeHistory = 1)
+ OPTION (MAXDOP 1)
ELSE
SELECT ResourceTypeId
,ResourceId
,ResourceSurrogateId
,Version
,IsDeleted
- FROM (SELECT TOP (@DummyTop) * FROM @Keys) A
- JOIN dbo.Resource B ON ResourceTypeId = TypeId AND ResourceSurrogateId = SurrogateId
- WHERE IsHistory = 0 OR @IncludeHistory = 1
- OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ FROM dbo.Resource
+ WHERE TransactionId = @TransactionId AND (IsHistory = 0 OR @IncludeHistory = 1)
+ OPTION (MAXDOP 1)
EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='End',@Start=@st,@Rows=@@rowcount
END TRY
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResourcesByTypeAndSurrogateIdRange.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResourcesByTypeAndSurrogateIdRange.sql
index 4b8115c946..f2853edc60 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResourcesByTypeAndSurrogateIdRange.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/GetResourcesByTypeAndSurrogateIdRange.sql
@@ -1,6 +1,6 @@
--DROP PROCEDURE dbo.GetResourcesByTypeAndSurrogateIdRange
GO
-CREATE PROCEDURE dbo.GetResourcesByTypeAndSurrogateIdRange @ResourceTypeId smallint, @StartId bigint, @EndId bigint, @GlobalEndId bigint = NULL, @IncludeHistory bit = 0, @IncludeDeleted bit = 0
+CREATE PROCEDURE dbo.GetResourcesByTypeAndSurrogateIdRange @ResourceTypeId smallint, @StartId bigint, @EndId bigint, @GlobalEndId bigint = NULL, @IncludeHistory bit = 1, @IncludeDeleted bit = 1
AS
set nocount on
DECLARE @SP varchar(100) = 'GetResourcesByTypeAndSurrogateIdRange'
@@ -9,18 +9,19 @@ DECLARE @SP varchar(100) = 'GetResourcesByTypeAndSurrogateIdRange'
+' E='+isnull(convert(varchar,@EndId),'NULL')
+' GE='+isnull(convert(varchar,@GlobalEndId),'NULL')
+' HI='+isnull(convert(varchar,@IncludeHistory),'NULL')
- +' DE'+isnull(convert(varchar,@IncludeDeleted),'NULL')
+ +' DE='+isnull(convert(varchar,@IncludeDeleted),'NULL')
,@st datetime = getUTCdate()
,@DummyTop bigint = 9223372036854775807
+ ,@Rows int
BEGIN TRY
- DECLARE @ResourceIds TABLE (ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS PRIMARY KEY)
+ DECLARE @ResourceIdInts TABLE (ResourceIdInt bigint PRIMARY KEY)
DECLARE @SurrogateIds TABLE (MaxSurrogateId bigint PRIMARY KEY)
IF @GlobalEndId IS NOT NULL AND @IncludeHistory = 0 -- snapshot view
BEGIN
- INSERT INTO @ResourceIds
- SELECT DISTINCT ResourceId
+ INSERT INTO @ResourceIdInts
+ SELECT DISTINCT ResourceIdInt
FROM dbo.Resource
WHERE ResourceTypeId = @ResourceTypeId
AND ResourceSurrogateId BETWEEN @StartId AND @EndId
@@ -31,10 +32,10 @@ BEGIN TRY
IF @@rowcount > 0
INSERT INTO @SurrogateIds
SELECT ResourceSurrogateId
- FROM (SELECT ResourceId, ResourceSurrogateId, RowId = row_number() OVER (PARTITION BY ResourceId ORDER BY ResourceSurrogateId DESC)
- FROM dbo.Resource WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId_Version) -- w/o hint access to Resource table is inefficient when many versions are present. Hint is ignored if Resource is a view.
+ FROM (SELECT ResourceIdInt, ResourceSurrogateId, RowId = row_number() OVER (PARTITION BY ResourceIdInt ORDER BY ResourceSurrogateId DESC)
+ FROM dbo.Resource
WHERE ResourceTypeId = @ResourceTypeId
- AND ResourceId IN (SELECT TOP (@DummyTop) ResourceId FROM @ResourceIds)
+ AND ResourceIdInt IN (SELECT TOP (@DummyTop) ResourceIdInt FROM @ResourceIdInts)
AND ResourceSurrogateId BETWEEN @StartId AND @GlobalEndId
) A
WHERE RowId = 1
@@ -42,19 +43,33 @@ BEGIN TRY
OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
END
- SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource
- FROM dbo.Resource
- WHERE ResourceTypeId = @ResourceTypeId
- AND ResourceSurrogateId BETWEEN @StartId AND @EndId
- AND (IsHistory = 0 OR @IncludeHistory = 1)
- AND (IsDeleted = 0 OR @IncludeDeleted = 1)
- UNION ALL
- SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource
- FROM @SurrogateIds
- JOIN dbo.Resource ON ResourceTypeId = @ResourceTypeId AND ResourceSurrogateId = MaxSurrogateId
- WHERE IsHistory = 1
- AND (IsDeleted = 0 OR @IncludeDeleted = 1)
- OPTION (MAXDOP 1)
+ IF @IncludeHistory = 0
+ SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource, FileId, OffsetInFile
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ AND IsHistory = 0
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ UNION ALL
+ SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource, FileId, OffsetInFile
+ FROM @SurrogateIds
+ JOIN dbo.Resource ON ResourceTypeId = @ResourceTypeId AND ResourceSurrogateId = MaxSurrogateId
+ WHERE IsHistory = 1
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ OPTION (MAXDOP 1, LOOP JOIN)
+ ELSE -- @IncludeHistory = 1
+ SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource, FileId, OffsetInFile
+ FROM dbo.Resource
+ WHERE ResourceTypeId = @ResourceTypeId
+ AND ResourceSurrogateId BETWEEN @StartId AND @EndId
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ UNION ALL
+ SELECT ResourceTypeId, ResourceId, Version, IsDeleted, ResourceSurrogateId, RequestMethod, IsMatch = convert(bit,1), IsPartial = convert(bit,0), IsRawResourceMetaSet, SearchParamHash, RawResource, FileId, OffsetInFile
+ FROM @SurrogateIds
+ JOIN dbo.Resource ON ResourceTypeId = @ResourceTypeId AND ResourceSurrogateId = MaxSurrogateId
+ WHERE IsHistory = 1
+ AND (IsDeleted = 0 OR @IncludeDeleted = 1)
+ OPTION (MAXDOP 1, LOOP JOIN)
EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='End',@Start=@st,@Rows=@@rowcount
END TRY
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/HardDeleteResource.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/HardDeleteResource.sql
index 6b85ea2747..24dd4ef06d 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/HardDeleteResource.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/HardDeleteResource.sql
@@ -2,70 +2,137 @@
@ResourceTypeId smallint
,@ResourceId varchar(64)
,@KeepCurrentVersion bit
- ,@IsResourceChangeCaptureEnabled bit
+ ,@IsResourceChangeCaptureEnabled bit = 0 -- TODO: Remove input parameter after deployment
+ ,@MakeResourceInvisible bit = 0
AS
set nocount on
DECLARE @SP varchar(100) = object_name(@@procid)
- ,@Mode varchar(200) = 'RT='+convert(varchar,@ResourceTypeId)+' R='+@ResourceId+' V='+convert(varchar,@KeepCurrentVersion)+' CC='+convert(varchar,@IsResourceChangeCaptureEnabled)
+ ,@Mode varchar(200) = 'RT='+convert(varchar,@ResourceTypeId)+' R='+@ResourceId+' V='+convert(varchar,@KeepCurrentVersion)
,@st datetime = getUTCdate()
,@TransactionId bigint
+ ,@DeletedIdMap int = 0
+ ,@Rows int
-BEGIN TRY
- IF @IsResourceChangeCaptureEnabled = 1 EXECUTE dbo.MergeResourcesBeginTransaction @Count = 1, @TransactionId = @TransactionId OUT
+IF @IsResourceChangeCaptureEnabled = 1
+ SET @MakeResourceInvisible = 1
- IF @KeepCurrentVersion = 0
- BEGIN TRANSACTION
+SET @Mode += ' I='+convert(varchar,@MakeResourceInvisible)
- DECLARE @SurrogateIds TABLE (ResourceSurrogateId BIGINT NOT NULL)
+IF @MakeResourceInvisible = 1
+BEGIN
+ EXECUTE dbo.MergeResourcesBeginTransaction @Count = 1, @TransactionId = @TransactionId OUT
+ SET @Mode += ' T='+convert(varchar,@TransactionId)
+END
- IF @IsResourceChangeCaptureEnabled = 1 AND NOT EXISTS (SELECT * FROM dbo.Parameters WHERE Id = 'InvisibleHistory.IsEnabled' AND Number = 0)
+DECLARE @Ids TABLE (ResourceSurrogateId bigint NOT NULL, ResourceIdInt bigint NOT NULL)
+DECLARE @IdsDistinct TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL PRIMARY KEY (ResourceTypeId, ResourceIdInt))
+DECLARE @RefIdsRaw TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL)
+
+RetryResourceIdIntMapLogic:
+BEGIN TRY
+ BEGIN TRANSACTION
+
+ IF @MakeResourceInvisible = 1
UPDATE dbo.Resource
SET IsDeleted = 1
,RawResource = 0xF -- invisible value
,SearchParamHash = NULL
,HistoryTransactionId = @TransactionId
- OUTPUT deleted.ResourceSurrogateId INTO @SurrogateIds
+ OUTPUT deleted.ResourceSurrogateId, deleted.ResourceIdInt INTO @Ids
WHERE ResourceTypeId = @ResourceTypeId
AND ResourceId = @ResourceId
AND (@KeepCurrentVersion = 0 OR IsHistory = 1)
- AND RawResource <> 0xF
+ AND (RawResource IS NULL -- stored in ADLS
+ OR RawResource <> 0xF -- stored in the database and not already invisible
+ )
ELSE
+ BEGIN
DELETE dbo.Resource
- OUTPUT deleted.ResourceSurrogateId INTO @SurrogateIds
+ OUTPUT deleted.ResourceSurrogateId, deleted.ResourceIdInt INTO @Ids
WHERE ResourceTypeId = @ResourceTypeId
AND ResourceId = @ResourceId
AND (@KeepCurrentVersion = 0 OR IsHistory = 1)
AND RawResource <> 0xF
+ INSERT INTO @IdsDistinct SELECT DISTINCT @ResourceTypeId, ResourceIdInt FROM @Ids
+ SET @Rows = @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM A FROM @IdsDistinct A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM A FROM @IdsDistinct A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM B FROM @IdsDistinct A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap = @@rowcount
+ END
+ END
+ END
+ END
+
IF @KeepCurrentVersion = 0
BEGIN
-- PAGLOCK allows deallocation of empty page without waiting for ghost cleanup
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.ResourceWriteClaim B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.ReferenceSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.TokenSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.TokenText B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.StringSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.UriSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.NumberSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.QuantitySearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.DateTimeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.ReferenceTokenCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.TokenTokenCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.TokenDateTimeCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.TokenQuantityCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.TokenStringCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
- DELETE FROM B FROM @SurrogateIds A INNER LOOP JOIN dbo.TokenNumberNumberCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ResourceWriteClaim B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B
+ OUTPUT deleted.ReferenceResourceTypeId, deleted.ReferenceResourceIdInt INTO @RefIdsRaw
+ FROM @Ids A INNER LOOP JOIN dbo.ResourceReferenceSearchParams B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM @IdsDistinct -- is used above
+ INSERT INTO @IdsDistinct SELECT DISTINCT ResourceTypeId, ResourceIdInt FROM @RefIdsRaw
+ SET @Rows = @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM A FROM @IdsDistinct A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM A FROM @IdsDistinct A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ DELETE FROM B FROM @IdsDistinct A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap += @@rowcount
+ END
+ END
+ END
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.StringReferenceSearchParams B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenText B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.StringSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.UriSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.NumberSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.QuantitySearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.DateTimeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ReferenceTokenCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenTokenCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenDateTimeCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenQuantityCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenStringCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenNumberNumberCompositeSearchParam B WITH (INDEX = 1, FORCESEEK, PAGLOCK) ON B.ResourceTypeId = @ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId OPTION (MAXDOP 1)
END
- IF @@trancount > 0 COMMIT TRANSACTION
+ COMMIT TRANSACTION
- IF @IsResourceChangeCaptureEnabled = 1 EXECUTE dbo.MergeResourcesCommitTransaction @TransactionId
+ IF @MakeResourceInvisible = 1
+ EXECUTE dbo.MergeResourcesCommitTransaction @TransactionId
- EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='End',@Start=@st
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='End',@Start=@st,@Text=@DeletedIdMap
END TRY
BEGIN CATCH
IF @@trancount > 0 ROLLBACK TRANSACTION
- EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Error',@Start=@st;
- THROW
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Error',@Start=@st
+
+ IF error_number() = 547 AND error_message() LIKE '%DELETE%'-- reference violation on DELETE
+ BEGIN
+ DELETE FROM @Ids
+ DELETE FROM @RefIdsRaw
+ DELETE FROM @IdsDistinct
+ GOTO RetryResourceIdIntMapLogic
+ END
+ ELSE
+ THROW
END CATCH
GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/MergeResources.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/MergeResources.sql
index d6e4ca76ed..166371b66d 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/MergeResources.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/MergeResources.sql
@@ -10,7 +10,8 @@ CREATE PROCEDURE dbo.MergeResources
,@IsResourceChangeCaptureEnabled bit = 0
,@TransactionId bigint = NULL
,@SingleTransaction bit = 1
- ,@Resources dbo.ResourceList READONLY
+ ,@Resources dbo.ResourceList READONLY -- before lake code. TODO: Remove after deployment
+ ,@ResourcesLake dbo.ResourceListLake READONLY -- Lake code
,@ResourceWriteClaims dbo.ResourceWriteClaimList READONLY
,@ReferenceSearchParams dbo.ReferenceSearchParamList READONLY
,@TokenSearchParams dbo.TokenSearchParamList READONLY
@@ -33,13 +34,144 @@ DECLARE @st datetime = getUTCdate()
,@DummyTop bigint = 9223372036854775807
,@InitialTranCount int = @@trancount
,@IsRetry bit = 0
+ ,@RT smallint
+ ,@NewIdsCount int
+ ,@FirstIdInt bigint
+ ,@CurrentRows int
+ ,@DeletedIdMap int
-DECLARE @Mode varchar(200) = isnull((SELECT 'RT=['+convert(varchar,min(ResourceTypeId))+','+convert(varchar,max(ResourceTypeId))+'] Sur=['+convert(varchar,min(ResourceSurrogateId))+','+convert(varchar,max(ResourceSurrogateId))+'] V='+convert(varchar,max(Version))+' Rows='+convert(varchar,count(*)) FROM @Resources),'Input=Empty')
+DECLARE @Mode varchar(200) = isnull((SELECT 'RT=['+convert(varchar,min(ResourceTypeId))+','+convert(varchar,max(ResourceTypeId))+'] Sur=['+convert(varchar,min(ResourceSurrogateId))+','+convert(varchar,max(ResourceSurrogateId))+'] V='+convert(varchar,max(Version))+' Rows='+convert(varchar,count(*)) FROM (SELECT ResourceTypeId, ResourceSurrogateId, Version FROM @Resources UNION ALL SELECT ResourceTypeId, ResourceSurrogateId, Version FROM @ResourcesLake) A),'Input=Empty')
SET @Mode += ' E='+convert(varchar,@RaiseExceptionOnConflict)+' CC='+convert(varchar,@IsResourceChangeCaptureEnabled)+' IT='+convert(varchar,@InitialTranCount)+' T='+isnull(convert(varchar,@TransactionId),'NULL')
SET @AffectedRows = 0
+RetryResourceIdIntMapLogic:
BEGIN TRY
+ DECLARE @InputIds AS TABLE (ResourceTypeId smallint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @CurrentRefIdsRaw TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL)
+ DECLARE @CurrentRefIds TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL PRIMARY KEY (ResourceTypeId, ResourceIdInt))
+ DECLARE @ExistingIdsReference AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ExistingIdsResource AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertIds AS TABLE (ResourceTypeId smallint NOT NULL, IdIndex int NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertedIdsReference AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertedIdsResource AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ResourcesWithIds AS TABLE
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL
+ ,ResourceIdInt bigint NOT NULL
+ ,Version int NOT NULL
+ ,HasVersionToCompare bit NOT NULL -- in case of multiple versions per resource indicates that row contains (existing version + 1) value
+ ,IsDeleted bit NOT NULL
+ ,IsHistory bit NOT NULL
+ ,KeepHistory bit NOT NULL
+ ,RawResource varbinary(max) NULL
+ ,IsRawResourceMetaSet bit NOT NULL
+ ,RequestMethod varchar(10) NULL
+ ,SearchParamHash varchar(64) NULL
+ ,FileId bigint NULL
+ ,OffsetInFile int NULL
+
+ PRIMARY KEY (ResourceTypeId, ResourceSurrogateId)
+ ,UNIQUE (ResourceTypeId, ResourceIdInt, Version)
+ )
+ DECLARE @ReferenceSearchParamsWithIds AS TABLE
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,SearchParamId smallint NOT NULL
+ ,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
+ ,ReferenceResourceTypeId smallint NOT NULL
+ ,ReferenceResourceIdInt bigint NOT NULL
+
+ UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt)
+ )
+
+ -- Prepare id map for reference search params Start ---------------------------------------------------------------------------
+ INSERT INTO @InputIds SELECT DISTINCT ReferenceResourceTypeId, ReferenceResourceId FROM @ReferenceSearchParams WHERE ReferenceResourceTypeId IS NOT NULL
+
+ INSERT INTO @ExistingIdsReference
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT A.ResourceTypeId, ResourceIdInt, A.ResourceId
+ FROM @InputIds A
+ JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+
+ INSERT INTO @InsertIds
+ ( ResourceTypeId, IdIndex, ResourceId )
+ SELECT ResourceTypeId, row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1, ResourceId
+ FROM @InputIds A
+ WHERE NOT EXISTS (SELECT * FROM @ExistingIdsReference B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId)
+
+ SET @NewIdsCount = (SELECT count(*) FROM @InsertIds)
+ IF @NewIdsCount > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @NewIdsCount, @FirstIdInt OUT
+
+ INSERT INTO @InsertedIdsReference
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, IdIndex + @FirstIdInt, ResourceId
+ FROM @InsertIds
+ END
+
+ INSERT INTO @ReferenceSearchParamsWithIds
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT A.ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, isnull(C.ResourceIdInt,B.ResourceIdInt)
+ FROM @ReferenceSearchParams A
+ LEFT OUTER JOIN @InsertedIdsReference B ON B.ResourceTypeId = A.ReferenceResourceTypeId AND B.ResourceId = A.ReferenceResourceId
+ LEFT OUTER JOIN @ExistingIdsReference C ON C.ResourceTypeId = A.ReferenceResourceTypeId AND C.ResourceId = A.ReferenceResourceId
+ WHERE ReferenceResourceTypeId IS NOT NULL
+ -- Prepare id map for reference search params End ---------------------------------------------------------------------------
+
+ -- Prepare id map for resources Start ---------------------------------------------------------------------------
+ DELETE FROM @InputIds
+ IF EXISTS (SELECT * FROM @ResourcesLake)
+ INSERT INTO @InputIds SELECT ResourceTypeId, ResourceId FROM @ResourcesLake GROUP BY ResourceTypeId, ResourceId
+ ELSE
+ INSERT INTO @InputIds SELECT ResourceTypeId, ResourceId FROM @Resources GROUP BY ResourceTypeId, ResourceId
+
+ INSERT INTO @ExistingIdsResource
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT A.ResourceTypeId, isnull(C.ResourceIdInt,B.ResourceIdInt), A.ResourceId
+ FROM @InputIds A
+ LEFT OUTER JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN @InsertedIdsReference C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceId = A.ResourceId
+ WHERE C.ResourceIdInt IS NOT NULL OR B.ResourceIdInt IS NOT NULL
+
+ DELETE FROM @InsertIds
+ INSERT INTO @InsertIds
+ ( ResourceTypeId, IdIndex, ResourceId )
+ SELECT ResourceTypeId, row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1, ResourceId
+ FROM @InputIds A
+ WHERE NOT EXISTS (SELECT * FROM @ExistingIdsResource B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId)
+
+ SET @NewIdsCount = (SELECT count(*) FROM @InsertIds)
+ IF @NewIdsCount > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @NewIdsCount, @FirstIdInt OUT
+
+ INSERT INTO @InsertedIdsResource
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, IdIndex + @FirstIdInt, ResourceId
+ FROM @InsertIds
+ END
+
+ IF EXISTS (SELECT * FROM @ResourcesLake)
+ INSERT INTO @ResourcesWithIds
+ ( ResourceTypeId, ResourceId, ResourceIdInt, Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, FileId, OffsetInFile )
+ SELECT A.ResourceTypeId, A.ResourceId, isnull(C.ResourceIdInt,B.ResourceIdInt), Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, FileId, OffsetInFile
+ FROM @ResourcesLake A
+ LEFT OUTER JOIN @InsertedIdsResource B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN @ExistingIdsResource C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceId = A.ResourceId
+ ELSE
+ INSERT INTO @ResourcesWithIds
+ ( ResourceTypeId, ResourceId, ResourceIdInt, Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, FileId, OffsetInFile )
+ SELECT A.ResourceTypeId, A.ResourceId, isnull(C.ResourceIdInt,B.ResourceIdInt), Version, HasVersionToCompare, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, KeepHistory, RawResource, IsRawResourceMetaSet, SearchParamHash, NULL, NULL
+ FROM @Resources A
+ LEFT OUTER JOIN @InsertedIdsResource B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+ LEFT OUTER JOIN @ExistingIdsResource C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceId = A.ResourceId
+ -- Prepare id map for resources End ---------------------------------------------------------------------------
+
DECLARE @Existing AS TABLE (ResourceTypeId smallint NOT NULL, SurrogateId bigint NOT NULL PRIMARY KEY (ResourceTypeId, SurrogateId))
DECLARE @ResourceInfos AS TABLE
@@ -65,8 +197,8 @@ BEGIN TRY
IF @InitialTranCount = 0
BEGIN
IF EXISTS (SELECT * -- This extra statement avoids putting range locks when we don't need them
- FROM @Resources A JOIN dbo.Resource B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- --WHERE B.IsHistory = 0 -- With this clause wrong plans are created on empty/small database. Commented until resource separation is in place.
+ FROM @ResourcesWithIds A JOIN dbo.Resource B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ WHERE B.IsHistory = 0
)
BEGIN
BEGIN TRANSACTION
@@ -74,14 +206,14 @@ BEGIN TRY
INSERT INTO @Existing
( ResourceTypeId, SurrogateId )
SELECT B.ResourceTypeId, B.ResourceSurrogateId
- FROM (SELECT TOP (@DummyTop) * FROM @Resources) A
+ FROM (SELECT TOP (@DummyTop) * FROM @ResourcesWithIds) A
JOIN dbo.Resource B WITH (ROWLOCK, HOLDLOCK) ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
WHERE B.IsHistory = 0
AND B.ResourceId = A.ResourceId
AND B.Version = A.Version
OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
- IF @@rowcount = (SELECT count(*) FROM @Resources) SET @IsRetry = 1
+ IF @@rowcount = (SELECT count(*) FROM @ResourcesWithIds) SET @IsRetry = 1
IF @IsRetry = 0 COMMIT TRANSACTION -- commit check transaction
END
@@ -96,9 +228,9 @@ BEGIN TRY
INSERT INTO @ResourceInfos
( ResourceTypeId, SurrogateId, Version, KeepHistory, PreviousVersion, PreviousSurrogateId )
SELECT A.ResourceTypeId, A.ResourceSurrogateId, A.Version, A.KeepHistory, B.Version, B.ResourceSurrogateId
- FROM (SELECT TOP (@DummyTop) * FROM @Resources WHERE HasVersionToCompare = 1) A
- LEFT OUTER JOIN dbo.Resource B -- WITH (UPDLOCK, HOLDLOCK) These locking hints cause deadlocks and are not needed. Racing might lead to tries to insert dups in unique index (with version key), but it will fail anyway, and in no case this will cause incorrect data saved.
- ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.IsHistory = 0
+ FROM (SELECT TOP (@DummyTop) * FROM @ResourcesWithIds WHERE HasVersionToCompare = 1) A
+ LEFT OUTER JOIN dbo.CurrentResources B -- WITH (UPDLOCK, HOLDLOCK) These locking hints cause deadlocks and are not needed. Racing might lead to tries to insert dups in unique index (with version key), but it will fail anyway, and in no case this will cause incorrect data saved.
+ ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
IF @RaiseExceptionOnConflict = 1 AND EXISTS (SELECT * FROM @ResourceInfos WHERE PreviousVersion IS NOT NULL AND Version <= PreviousVersion)
@@ -129,7 +261,39 @@ BEGIN TRY
DELETE FROM dbo.ResourceWriteClaim WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE SurrogateId = ResourceSurrogateId)
SET @AffectedRows += @@rowcount
- DELETE FROM dbo.ReferenceSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ DELETE FROM dbo.ResourceReferenceSearchParams
+ OUTPUT deleted.ReferenceResourceTypeId, deleted.ReferenceResourceIdInt INTO @CurrentRefIdsRaw
+ WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
+ SET @CurrentRows = @@rowcount
+ SET @AffectedRows += @CurrentRows
+ -- start deleting from ResourceIdIntMap
+ INSERT INTO @CurrentRefIds SELECT DISTINCT ResourceTypeId, ResourceIdInt FROM @CurrentRefIdsRaw
+ SET @CurrentRows = @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove not reused
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM @ReferenceSearchParamsWithIds B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove referenced in Resources
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove still referenced in ResourceReferenceSearchParams
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- delete from id map
+ DELETE FROM B FROM @CurrentRefIds A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap = @@rowcount
+ END
+ END
+ END
+ END
+ DELETE FROM dbo.StringReferenceSearchParams WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
SET @AffectedRows += @@rowcount
DELETE FROM dbo.TokenSearchParam WHERE EXISTS (SELECT * FROM @PreviousSurrogateIds WHERE TypeId = ResourceTypeId AND SurrogateId = ResourceSurrogateId)
SET @AffectedRows += @@rowcount
@@ -161,10 +325,20 @@ BEGIN TRY
--EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Info',@Start=@st,@Rows=@AffectedRows,@Text='Old rows'
END
+ INSERT INTO dbo.ResourceIdIntMap
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, ResourceIdInt, ResourceId
+ FROM @InsertedIdsResource
+
+ INSERT INTO dbo.ResourceIdIntMap
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, ResourceIdInt, ResourceId
+ FROM @InsertedIdsReference
+
INSERT INTO dbo.Resource
- ( ResourceTypeId, ResourceId, Version, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, RawResource, IsRawResourceMetaSet, SearchParamHash, TransactionId )
- SELECT ResourceTypeId, ResourceId, Version, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, RawResource, IsRawResourceMetaSet, SearchParamHash, @TransactionId
- FROM @Resources
+ ( ResourceTypeId, ResourceIdInt, Version, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, RawResource, IsRawResourceMetaSet, SearchParamHash, TransactionId, FileId, OffsetInFile )
+ SELECT ResourceTypeId, ResourceIdInt, Version, IsHistory, ResourceSurrogateId, IsDeleted, RequestMethod, RawResource, IsRawResourceMetaSet, SearchParamHash, @TransactionId, FileId, OffsetInFile
+ FROM @ResourcesWithIds
SET @AffectedRows += @@rowcount
INSERT INTO dbo.ResourceWriteClaim
@@ -173,10 +347,17 @@ BEGIN TRY
FROM @ResourceWriteClaims
SET @AffectedRows += @@rowcount
- INSERT INTO dbo.ReferenceSearchParam
- ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceId, ReferenceResourceVersion )
- SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceId, ReferenceResourceVersion
+ INSERT INTO dbo.ResourceReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt
+ FROM @ReferenceSearchParamsWithIds
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.StringReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId
FROM @ReferenceSearchParams
+ WHERE ReferenceResourceTypeId IS NULL
SET @AffectedRows += @@rowcount
INSERT INTO dbo.TokenSearchParam
@@ -268,12 +449,21 @@ BEGIN TRY
OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
SET @AffectedRows += @@rowcount
- INSERT INTO dbo.ReferenceSearchParam
- ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceId, ReferenceResourceVersion )
- SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceId, ReferenceResourceVersion
- FROM (SELECT TOP (@DummyTop) * FROM @ReferenceSearchParams) A
+ INSERT INTO dbo.ResourceReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT A.ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt
+ FROM (SELECT TOP (@DummyTop) * FROM @ReferenceSearchParamsWithIds) A
WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
- AND NOT EXISTS (SELECT * FROM dbo.ReferenceSearchParam C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
+ OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
+ SET @AffectedRows += @@rowcount
+
+ INSERT INTO dbo.StringReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId )
+ SELECT A.ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId
+ FROM (SELECT TOP (@DummyTop) * FROM @ReferenceSearchParams WHERE ReferenceResourceTypeId IS NULL) A
+ WHERE EXISTS (SELECT * FROM @Existing B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.SurrogateId = A.ResourceSurrogateId)
+ AND NOT EXISTS (SELECT * FROM dbo.StringReferenceSearchParams C WHERE C.ResourceTypeId = A.ResourceTypeId AND C.ResourceSurrogateId = A.ResourceSurrogateId)
OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
SET @AffectedRows += @@rowcount
@@ -396,24 +586,44 @@ BEGIN TRY
END
IF @IsResourceChangeCaptureEnabled = 1 --If the resource change capture feature is enabled, to execute a stored procedure called CaptureResourceChanges to insert resource change data.
- EXECUTE dbo.CaptureResourceIdsForChanges @Resources
+ EXECUTE dbo.CaptureResourceIdsForChanges @Resources, @ResourcesLake
IF @TransactionId IS NOT NULL
EXECUTE dbo.MergeResourcesCommitTransaction @TransactionId
IF @InitialTranCount = 0 AND @@trancount > 0 COMMIT TRANSACTION
- EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='End',@Start=@st,@Rows=@AffectedRows
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='End',@Start=@st,@Rows=@AffectedRows,@Text=@DeletedIdMap
END TRY
BEGIN CATCH
IF @InitialTranCount = 0 AND @@trancount > 0 ROLLBACK TRANSACTION
IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
- EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Error',@Start=@st;
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Error',@Start=@st
- IF @RaiseExceptionOnConflict = 1 AND error_number() IN (2601, 2627) AND error_message() LIKE '%''dbo.Resource''%'
- THROW 50409, 'Resource has been recently updated or added, please compare the resource content in code for any duplicate updates', 1;
- ELSE
- THROW
+ IF error_number() IN (2601, 2627) AND error_message() LIKE '%''dbo.ResourceIdIntMap''%' -- pk violation
+ OR error_number() = 547 AND error_message() LIKE '%DELETE%' -- reference violation on DELETE
+ BEGIN
+ DELETE FROM @ResourcesWithIds
+ DELETE FROM @ReferenceSearchParamsWithIds
+ DELETE FROM @CurrentRefIdsRaw
+ DELETE FROM @CurrentRefIds
+ DELETE FROM @InputIds
+ DELETE FROM @InsertIds
+ DELETE FROM @InsertedIdsReference
+ DELETE FROM @ExistingIdsReference
+ DELETE FROM @InsertedIdsResource
+ DELETE FROM @ExistingIdsResource
+ DELETE FROM @Existing
+ DELETE FROM @ResourceInfos
+ DELETE FROM @PreviousSurrogateIds
+
+ GOTO RetryResourceIdIntMapLogic
+ END
+ ELSE
+ IF @RaiseExceptionOnConflict = 1 AND error_number() IN (2601, 2627) AND (error_message() LIKE '%''dbo.Resource%' OR error_message() LIKE '%''dbo.CurrentResources%' OR error_message() LIKE '%''dbo.HistoryResources%' OR error_message() LIKE '%''dbo.RawResources''%')
+ THROW 50409, 'Resource has been recently updated or added, please compare the resource content in code for any duplicate updates', 1;
+ ELSE
+ THROW
END CATCH
GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/MergeResourcesDeleteInvisibleHistory.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/MergeResourcesDeleteInvisibleHistory.sql
index 9ae6120539..827e38268a 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/MergeResourcesDeleteInvisibleHistory.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/MergeResourcesDeleteInvisibleHistory.sql
@@ -5,30 +5,64 @@ AS
set nocount on
DECLARE @SP varchar(100) = object_name(@@procid)
,@Mode varchar(100) = 'T='+convert(varchar,@TransactionId)
- ,@st datetime = getUTCdate()
- ,@TypeId smallint
+ ,@st datetime
+ ,@Rows int
+ ,@DeletedIdMap int
SET @AffectedRows = 0
-BEGIN TRY
- DECLARE @Types TABLE (TypeId smallint PRIMARY KEY, Name varchar(100))
- INSERT INTO @Types EXECUTE dbo.GetUsedResourceTypes
+Retry:
+BEGIN TRY
+ DECLARE @Ids TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL)
+
+ BEGIN TRANSACTION
- WHILE EXISTS (SELECT * FROM @Types)
- BEGIN
- SET @TypeId = (SELECT TOP 1 TypeId FROM @Types ORDER BY TypeId)
-
- DELETE FROM dbo.Resource WHERE ResourceTypeId = @TypeId AND HistoryTransactionId = @TransactionId AND RawResource = 0xF
- SET @AffectedRows += @@rowcount
+ SET @st = getUTCdate()
+ DELETE FROM A
+ OUTPUT deleted.ResourceTypeId, deleted.ResourceIdInt INTO @Ids
+ FROM dbo.Resource A
+ WHERE HistoryTransactionId = @TransactionId -- requires updated statistics
+ SET @Rows = @@rowcount
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Run',@Target='Resource',@Action='Delete',@Start=@st,@Rows=@Rows
+ SET @AffectedRows += @Rows
- DELETE FROM @Types WHERE TypeId = @TypeId
+ SET @st = getUTCdate()
+ IF @Rows > 0
+ BEGIN
+ -- remove referenced in resources
+ DELETE FROM A FROM @Ids A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ -- remove referenced in reference search params
+ DELETE FROM A FROM @Ids A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @Rows -= @@rowcount
+ IF @Rows > 0
+ BEGIN
+ -- delete from id map
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap = @@rowcount
+ END
+ END
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Run',@Target='ResourceIdIntMap',@Action='Delete',@Start=@st,@Rows=@DeletedIdMap
END
- EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='End',@Start=@st,@Rows=@AffectedRows
+ COMMIT TRANSACTION
+
+ SET @st = getUTCdate()
+ UPDATE dbo.Resource SET TransactionId = NULL WHERE TransactionId = @TransactionId
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='End',@Target='Resource',@Action='Update',@Start=@st,@Rows=@@rowcount
END TRY
BEGIN CATCH
IF error_number() = 1750 THROW -- Real error is before 1750, cannot trap in SQL.
- EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Error';
- THROW
+ IF @@trancount > 0 ROLLBACK TRANSACTION
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Error'
+ IF error_number() = 547 AND error_message() LIKE '%DELETE%' -- reference violation on DELETE
+ BEGIN
+ DELETE FROM @Ids
+ GOTO Retry
+ END
+ ELSE
+ THROW
END CATCH
GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/UpdateResourceSearchParams.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/UpdateResourceSearchParams.sql
index 88f4c2bfdf..03f392222e 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/UpdateResourceSearchParams.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/UpdateResourceSearchParams.sql
@@ -1,6 +1,7 @@
CREATE PROCEDURE dbo.UpdateResourceSearchParams
@FailedResources int = 0 OUT
- ,@Resources dbo.ResourceList READONLY
+ ,@Resources dbo.ResourceList READONLY -- TODO: Remove after deployment
+ ,@ResourcesLake dbo.ResourceListLake READONLY
,@ResourceWriteClaims dbo.ResourceWriteClaimList READONLY
,@ReferenceSearchParams dbo.ReferenceSearchParamList READONLY
,@TokenSearchParams dbo.TokenSearchParamList READONLY
@@ -20,49 +21,156 @@ AS
set nocount on
DECLARE @st datetime = getUTCdate()
,@SP varchar(100) = object_name(@@procid)
- ,@Mode varchar(200) = isnull((SELECT 'RT=['+convert(varchar,min(ResourceTypeId))+','+convert(varchar,max(ResourceTypeId))+'] Sur=['+convert(varchar,min(ResourceSurrogateId))+','+convert(varchar,max(ResourceSurrogateId))+'] V='+convert(varchar,max(Version))+' Rows='+convert(varchar,count(*)) FROM @Resources),'Input=Empty')
- ,@Rows int
+ ,@Mode varchar(200) = isnull((SELECT 'RT=['+convert(varchar,min(ResourceTypeId))+','+convert(varchar,max(ResourceTypeId))+'] Sur=['+convert(varchar,min(ResourceSurrogateId))+','+convert(varchar,max(ResourceSurrogateId))+'] V='+convert(varchar,max(Version))+' Rows='+convert(varchar,count(*)) FROM (SELECT ResourceTypeId, ResourceSurrogateId, Version FROM @ResourcesLake UNION ALL SELECT ResourceTypeId, ResourceSurrogateId, Version FROM @Resources) A),'Input=Empty')
+ ,@ResourceRows int
+ ,@InsertRows int
+ ,@DeletedIdMap int
+ ,@FirstIdInt bigint
+ ,@CurrentRows int
+RetryResourceIdIntMapLogic:
BEGIN TRY
DECLARE @Ids TABLE (ResourceTypeId smallint NOT NULL, ResourceSurrogateId bigint NOT NULL)
+ DECLARE @CurrentRefIdsRaw TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL)
+ DECLARE @CurrentRefIds TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL PRIMARY KEY (ResourceTypeId, ResourceIdInt))
+ DECLARE @InputRefIds AS TABLE (ResourceTypeId smallint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ExistingRefIds AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertRefIds AS TABLE (ResourceTypeId smallint NOT NULL, IdIndex int NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @InsertedRefIds AS TABLE (ResourceTypeId smallint NOT NULL, ResourceIdInt bigint NOT NULL, ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL PRIMARY KEY (ResourceTypeId, ResourceId))
+ DECLARE @ReferenceSearchParamsWithIds AS TABLE
+ (
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,SearchParamId smallint NOT NULL
+ ,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
+ ,ReferenceResourceTypeId smallint NULL
+ ,ReferenceResourceIdInt bigint NOT NULL
+ ,ReferenceResourceVersion int NULL
+
+ UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt)
+ )
+
+ -- Prepare insert into ResourceIdIntMap outside of transaction to minimize blocking
+ INSERT INTO @InputRefIds SELECT DISTINCT ReferenceResourceTypeId, ReferenceResourceId FROM @ReferenceSearchParams WHERE ReferenceResourceTypeId IS NOT NULL
+
+ INSERT INTO @ExistingRefIds
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT A.ResourceTypeId, ResourceIdInt, A.ResourceId
+ FROM @InputRefIds A
+ JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId
+
+ INSERT INTO @InsertRefIds
+ ( ResourceTypeId, IdIndex, ResourceId )
+ SELECT ResourceTypeId, row_number() OVER (ORDER BY ResourceTypeId, ResourceId) - 1, ResourceId
+ FROM @InputRefIds A
+ WHERE NOT EXISTS (SELECT * FROM @ExistingRefIds B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId)
+
+ SET @InsertRows = (SELECT count(*) FROM @InsertRefIds)
+ IF @InsertRows > 0
+ BEGIN
+ EXECUTE dbo.AssignResourceIdInts @InsertRows, @FirstIdInt OUT
+
+ INSERT INTO @InsertedRefIds
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, IdIndex + @FirstIdInt, ResourceId
+ FROM @InsertRefIds
+ END
+
+ INSERT INTO @ReferenceSearchParamsWithIds
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt, ReferenceResourceVersion )
+ SELECT A.ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, isnull(C.ResourceIdInt,B.ResourceIdInt), ReferenceResourceVersion
+ FROM @ReferenceSearchParams A
+ LEFT OUTER JOIN @InsertedRefIds B ON B.ResourceTypeId = A.ReferenceResourceTypeId AND B.ResourceId = A.ReferenceResourceId
+ LEFT OUTER JOIN @ExistingRefIds C ON C.ResourceTypeId = A.ReferenceResourceTypeId AND C.ResourceId = A.ReferenceResourceId
BEGIN TRANSACTION
-- Update the search parameter hash value in the main resource table
- UPDATE B
- SET SearchParamHash = A.SearchParamHash
- OUTPUT deleted.ResourceTypeId, deleted.ResourceSurrogateId INTO @Ids
- FROM @Resources A JOIN dbo.Resource B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- WHERE B.IsHistory = 0
- SET @Rows = @@rowcount
+ IF EXISTS (SELECT * FROM @ResourcesLake)
+ UPDATE B
+ SET SearchParamHash = (SELECT SearchParamHash FROM @ResourcesLake A WHERE A.ResourceTypeId = B.ResourceTypeId AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ OUTPUT deleted.ResourceTypeId, deleted.ResourceSurrogateId INTO @Ids
+ FROM dbo.Resource B
+ WHERE EXISTS (SELECT * FROM @ResourcesLake A WHERE A.ResourceTypeId = B.ResourceTypeId AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ AND B.IsHistory = 0
+ ELSE
+ UPDATE B
+ SET SearchParamHash = (SELECT SearchParamHash FROM @Resources A WHERE A.ResourceTypeId = B.ResourceTypeId AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ OUTPUT deleted.ResourceTypeId, deleted.ResourceSurrogateId INTO @Ids
+ FROM dbo.Resource B
+ WHERE EXISTS (SELECT * FROM @Resources A WHERE A.ResourceTypeId = B.ResourceTypeId AND A.ResourceSurrogateId = B.ResourceSurrogateId)
+ AND B.IsHistory = 0
+ SET @ResourceRows = @@rowcount
-- First, delete all the search params of the resources to reindex.
- DELETE FROM B FROM @Ids A JOIN dbo.ResourceWriteClaim B ON B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.ReferenceSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.TokenSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.TokenText B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.StringSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.UriSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.NumberSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.QuantitySearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.DateTimeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.ReferenceTokenCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.TokenTokenCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.TokenDateTimeCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.TokenQuantityCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.TokenStringCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
- DELETE FROM B FROM @Ids A JOIN dbo.TokenNumberNumberCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ResourceWriteClaim B ON B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B
+ OUTPUT deleted.ReferenceResourceTypeId, deleted.ReferenceResourceIdInt INTO @CurrentRefIdsRaw
+ FROM @Ids A INNER LOOP JOIN dbo.ResourceReferenceSearchParams B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.StringReferenceSearchParams B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenText B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.StringSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.UriSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.NumberSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.QuantitySearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.DateTimeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.ReferenceTokenCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenTokenCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenDateTimeCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenQuantityCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenStringCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ DELETE FROM B FROM @Ids A INNER LOOP JOIN dbo.TokenNumberNumberCompositeSearchParam B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
-- Next, insert all the new search params.
INSERT INTO dbo.ResourceWriteClaim
( ResourceSurrogateId, ClaimTypeId, ClaimValue )
SELECT ResourceSurrogateId, ClaimTypeId, ClaimValue
FROM @ResourceWriteClaims
+
+ -- start delete logic from ResourceIdIntMap
+ INSERT INTO @CurrentRefIds SELECT DISTINCT ResourceTypeId, ResourceIdInt FROM @CurrentRefIdsRaw
+ SET @CurrentRows = @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove not reused
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM @ReferenceSearchParamsWithIds B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove referenced by resources
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM dbo.CurrentResources B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- remove referenced by reference search params
+ DELETE FROM A FROM @CurrentRefIds A WHERE EXISTS (SELECT * FROM dbo.ResourceReferenceSearchParams B WHERE B.ReferenceResourceTypeId = A.ResourceTypeId AND B.ReferenceResourceIdInt = A.ResourceIdInt)
+ SET @CurrentRows -= @@rowcount
+ IF @CurrentRows > 0
+ BEGIN
+ -- finally delete from id map
+ DELETE FROM B FROM @CurrentRefIds A INNER LOOP JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceIdInt = A.ResourceIdInt
+ SET @DeletedIdMap = @@rowcount
+ END
+ END
+ END
+ END
- INSERT INTO dbo.ReferenceSearchParam
- ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceId, ReferenceResourceVersion )
- SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceId, ReferenceResourceVersion
+ INSERT INTO dbo.ResourceIdIntMap
+ ( ResourceTypeId, ResourceIdInt, ResourceId )
+ SELECT ResourceTypeId, ResourceIdInt, ResourceId
+ FROM @InsertedRefIds
+
+ INSERT INTO dbo.ResourceReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceIdInt
+ FROM @ReferenceSearchParamsWithIds
+
+ INSERT INTO dbo.StringReferenceSearchParams
+ ( ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId )
+ SELECT ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceId
FROM @ReferenceSearchParams
+ WHERE ReferenceResourceTypeId IS NULL
INSERT INTO dbo.TokenSearchParam
( ResourceTypeId, ResourceSurrogateId, SearchParamId, SystemId, Code, CodeOverflow )
@@ -131,13 +239,30 @@ BEGIN TRY
COMMIT TRANSACTION
- SET @FailedResources = (SELECT count(*) FROM @Resources) - @Rows
+ SET @FailedResources = (SELECT count(*) FROM @Resources) + (SELECT count(*) FROM @ResourcesLake) - @ResourceRows
- EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='End',@Start=@st,@Rows=@Rows
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='End',@Start=@st,@Rows=@ResourceRows,@Text=@DeletedIdMap
END TRY
BEGIN CATCH
IF @@trancount > 0 ROLLBACK TRANSACTION
- EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Error',@Start=@st;
- THROW
+
+ EXECUTE dbo.LogEvent @Process=@SP,@Mode=@Mode,@Status='Error',@Start=@st
+
+ IF error_number() IN (2601, 2627) AND error_message() LIKE '%''dbo.ResourceIdIntMap''%' -- pk violation
+ OR error_number() = 547 AND error_message() LIKE '%DELETE%' -- reference violation on DELETE
+ BEGIN
+ DELETE FROM @Ids
+ DELETE FROM @InputRefIds
+ DELETE FROM @CurrentRefIdsRaw
+ DELETE FROM @CurrentRefIds
+ DELETE FROM @ExistingRefIds
+ DELETE FROM @InsertRefIds
+ DELETE FROM @InsertedRefIds
+ DELETE FROM @ReferenceSearchParamsWithIds
+
+ GOTO RetryResourceIdIntMapLogic
+ END
+ ELSE
+ THROW
END CATCH
GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Tables/0_Resource.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Tables/0_Resource.sql
new file mode 100644
index 0000000000..01c48206fa
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Tables/0_Resource.sql
@@ -0,0 +1,122 @@
+-- 0 in this script name is needed to ensure that this script is executed first, so FK constraints can be created in other scripts
+-- Our code generator does not understand views, so we create tables which look like views and immediately drop them.
+CREATE TABLE dbo.CurrentResource
+(
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL
+ ,ResourceIdInt bigint NOT NULL
+ ,Version int NOT NULL
+ ,IsHistory bit NOT NULL
+ ,IsDeleted bit NOT NULL
+ ,RequestMethod varchar(10) NULL
+ ,RawResource varbinary(max) NULL
+ ,IsRawResourceMetaSet bit NOT NULL
+ ,SearchParamHash varchar(64) NULL
+ ,TransactionId bigint NULL
+ ,HistoryTransactionId bigint NULL
+ ,FileId bigint NULL
+ ,OffsetInFile int NULL
+)
+GO
+DROP TABLE dbo.CurrentResource
+GO
+CREATE TABLE dbo.Resource
+(
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL
+ ,ResourceIdInt bigint NOT NULL
+ ,Version int NOT NULL
+ ,IsHistory bit NOT NULL
+ ,IsDeleted bit NOT NULL
+ ,RequestMethod varchar(10) NULL
+ ,RawResource varbinary(max) NULL
+ ,IsRawResourceMetaSet bit NOT NULL
+ ,SearchParamHash varchar(64) NULL
+ ,TransactionId bigint NULL
+ ,HistoryTransactionId bigint NULL
+ ,FileId bigint NULL
+ ,OffsetInFile int NULL
+)
+CREATE INDEX IX_Resource_ResourceTypeId_ResourceSurrgateId ON Resource (ResourceTypeId) -- index definition does not matter as it is not used in new schema and left here just to generate same queries accessing old schema during upgrade.
+GO
+DROP TABLE dbo.Resource
+GO
+CREATE TABLE dbo.ResourceIdIntMap
+(
+ ResourceTypeId smallint NOT NULL
+ ,ResourceIdInt bigint NOT NULL
+ ,ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL
+
+ CONSTRAINT PKC_ResourceIdIntMap_ResourceIdInt_ResourceTypeId PRIMARY KEY CLUSTERED (ResourceIdInt, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+ ,CONSTRAINT U_ResourceIdIntMap_ResourceId_ResourceTypeId UNIQUE (ResourceId, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+)
+
+ALTER TABLE dbo.ResourceIdIntMap SET ( LOCK_ESCALATION = AUTO )
+GO
+CREATE TABLE dbo.RawResources
+(
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,RawResource varbinary(max) NULL
+
+ CONSTRAINT PKC_RawResources_ResourceTypeId_ResourceSurrogateId PRIMARY KEY CLUSTERED (ResourceTypeId, ResourceSurrogateId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+)
+
+ALTER TABLE dbo.RawResources SET ( LOCK_ESCALATION = AUTO )
+GO
+CREATE TABLE dbo.CurrentResources
+(
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,ResourceIdInt bigint NOT NULL
+ ,Version int NOT NULL
+ ,IsHistory bit NOT NULL CONSTRAINT DF_CurrentResources_IsHistory DEFAULT 0, CONSTRAINT CH_CurrentResources_IsHistory CHECK (IsHistory = 0)
+ ,IsDeleted bit NOT NULL
+ ,RequestMethod varchar(10) NULL
+ ,IsRawResourceMetaSet bit NOT NULL CONSTRAINT DF_CurrentResources_IsRawResourceMetaSet DEFAULT 0
+ ,SearchParamHash varchar(64) NULL
+ ,TransactionId bigint NULL
+ ,HistoryTransactionId bigint NULL
+ ,FileId bigint NULL
+ ,OffsetInFile int NULL
+
+ CONSTRAINT PKC_CurrentResources_ResourceTypeId_ResourceSurrogateId PRIMARY KEY CLUSTERED (ResourceTypeId, ResourceSurrogateId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+ ,CONSTRAINT U_CurrentResources_ResourceIdInt_ResourceTypeId UNIQUE (ResourceIdInt, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+)
+
+ALTER TABLE dbo.CurrentResources ADD CONSTRAINT FK_CurrentResources_ResourceIdInt_ResourceTypeId_ResourceIdIntMap FOREIGN KEY (ResourceIdInt, ResourceTypeId) REFERENCES dbo.ResourceIdIntMap (ResourceIdInt, ResourceTypeId)
+
+ALTER TABLE dbo.CurrentResources SET ( LOCK_ESCALATION = AUTO )
+
+CREATE INDEX IX_TransactionId_ResourceTypeId_WHERE_TransactionId_NOT_NULL ON dbo.CurrentResources (TransactionId, ResourceTypeId) WHERE TransactionId IS NOT NULL WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+CREATE INDEX IX_HistoryTransactionId_ResourceTypeId_WHERE_HistoryTransactionId_NOT_NULL ON dbo.CurrentResources (HistoryTransactionId, ResourceTypeId) WHERE HistoryTransactionId IS NOT NULL WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+GO
+CREATE TABLE dbo.HistoryResources
+(
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,ResourceIdInt bigint NOT NULL
+ ,Version int NOT NULL
+ ,IsHistory bit NOT NULL CONSTRAINT DF_HistoryResources_IsHistory DEFAULT 1, CONSTRAINT CH_HistoryResources_IsHistory CHECK (IsHistory = 1)
+ ,IsDeleted bit NOT NULL
+ ,RequestMethod varchar(10) NULL
+ ,IsRawResourceMetaSet bit NOT NULL CONSTRAINT DF_HistoryResources_IsRawResourceMetaSet DEFAULT 0
+ ,SearchParamHash varchar(64) NULL
+ ,TransactionId bigint NULL
+ ,HistoryTransactionId bigint NULL
+ ,FileId bigint NULL
+ ,OffsetInFile int NULL
+
+ CONSTRAINT PKC_HistoryResources_ResourceTypeId_ResourceSurrogateId PRIMARY KEY CLUSTERED (ResourceTypeId, ResourceSurrogateId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+ ,CONSTRAINT U_HistoryResources_ResourceIdInt_Version_ResourceTypeId UNIQUE (ResourceIdInt, Version, ResourceTypeId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+)
+
+ALTER TABLE dbo.HistoryResources ADD CONSTRAINT FK_HistoryResources_ResourceIdInt_ResourceTypeId_ResourceIdIntMap FOREIGN KEY (ResourceIdInt, ResourceTypeId) REFERENCES dbo.ResourceIdIntMap (ResourceIdInt, ResourceTypeId)
+
+ALTER TABLE dbo.HistoryResources SET ( LOCK_ESCALATION = AUTO )
+
+CREATE INDEX IX_TransactionId_ResourceTypeId_WHERE_TransactionId_NOT_NULL ON dbo.HistoryResources (TransactionId, ResourceTypeId) WHERE TransactionId IS NOT NULL WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+CREATE INDEX IX_HistoryTransactionId_ResourceTypeId_WHERE_HistoryTransactionId_NOT_NULL ON dbo.HistoryResources (HistoryTransactionId, ResourceTypeId) WHERE HistoryTransactionId IS NOT NULL WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Tables/ReferenceSearchParam.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Tables/ReferenceSearchParam.sql
index 42e498bb4c..d30f9dac97 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Tables/ReferenceSearchParam.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Tables/ReferenceSearchParam.sql
@@ -1,35 +1,58 @@
-CREATE TABLE dbo.ReferenceSearchParam
+-- Our code generator does not understand views (in the end ReferenceSearchParam is a view), so we create a table that looks like a view and immediately drop it.
+CREATE TABLE dbo.ReferenceSearchParam
(
- ResourceTypeId smallint NOT NULL,
- ResourceSurrogateId bigint NOT NULL,
- SearchParamId smallint NOT NULL,
- BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL,
- ReferenceResourceTypeId smallint NULL,
- ReferenceResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL,
- ReferenceResourceVersion int NULL
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,SearchParamId smallint NOT NULL
+ ,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
+ ,ReferenceResourceTypeId smallint NULL
+ ,ReferenceResourceId varchar(768) COLLATE Latin1_General_100_CS_AS NOT NULL
+ ,ReferenceResourceIdInt bigint NOT NULL
+ ,IsResourceRef bit NOT NULL
)
+GO
+DROP TABLE dbo.ReferenceSearchParam
+GO
+CREATE TABLE dbo.ResourceReferenceSearchParams
+(
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,SearchParamId smallint NOT NULL
+ ,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
+ ,ReferenceResourceTypeId smallint NOT NULL
+ ,ReferenceResourceIdInt bigint NOT NULL
+ ,IsResourceRef bit NOT NULL CONSTRAINT DF_ResourceReferenceSearchParams_IsResourceRef DEFAULT 1, CONSTRAINT CH_ResourceReferenceSearchParams_IsResourceRef CHECK (IsResourceRef = 1)
+)
+
+ALTER TABLE dbo.ResourceReferenceSearchParams ADD CONSTRAINT FK_ResourceReferenceSearchParams_ReferenceResourceIdInt_ReferenceResourceTypeId_ResourceIdIntMap FOREIGN KEY (ReferenceResourceIdInt, ReferenceResourceTypeId) REFERENCES dbo.ResourceIdIntMap (ResourceIdInt, ResourceTypeId)
-ALTER TABLE dbo.ReferenceSearchParam SET ( LOCK_ESCALATION = AUTO )
+ALTER TABLE dbo.ResourceReferenceSearchParams SET ( LOCK_ESCALATION = AUTO )
-CREATE CLUSTERED INDEX IXC_ReferenceSearchParam
-ON dbo.ReferenceSearchParam
+CREATE CLUSTERED INDEX IXC_ResourceSurrogateId_SearchParamId_ResourceTypeId
+ ON dbo.ResourceReferenceSearchParams (ResourceSurrogateId, SearchParamId, ResourceTypeId)
+ WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+
+CREATE UNIQUE INDEX IXU_ReferenceResourceIdInt_ReferenceResourceTypeId_SearchParamId_BaseUri_ResourceSurrogateId_ResourceTypeId
+ ON dbo.ResourceReferenceSearchParams (ReferenceResourceIdInt, ReferenceResourceTypeId, SearchParamId, BaseUri, ResourceSurrogateId, ResourceTypeId)
+ WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+GO
+CREATE TABLE dbo.StringReferenceSearchParams
(
- ResourceTypeId,
- ResourceSurrogateId,
- SearchParamId
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,SearchParamId smallint NOT NULL
+ ,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
+ ,ReferenceResourceId varchar(768) COLLATE Latin1_General_100_CS_AS NOT NULL
+ ,IsResourceRef bit NOT NULL CONSTRAINT DF_StringReferenceSearchParams_IsResourceRef DEFAULT 0, CONSTRAINT CH_StringReferenceSearchParams_IsResourceRef CHECK (IsResourceRef = 0)
)
-WITH (DATA_COMPRESSION = PAGE)
-ON PartitionScheme_ResourceTypeId(ResourceTypeId)
-CREATE UNIQUE INDEX IXU_ReferenceResourceId_ReferenceResourceTypeId_SearchParamId_BaseUri_ResourceSurrogateId_ResourceTypeId ON dbo.ReferenceSearchParam
- (
- ReferenceResourceId
- ,ReferenceResourceTypeId
- ,SearchParamId
- ,BaseUri
- ,ResourceSurrogateId
- ,ResourceTypeId
- )
- WITH (DATA_COMPRESSION = PAGE)
- ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+ALTER TABLE dbo.StringReferenceSearchParams SET ( LOCK_ESCALATION = AUTO )
+
+CREATE CLUSTERED INDEX IXC_ResourceSurrogateId_SearchParamId_ResourceTypeId
+ ON dbo.StringReferenceSearchParams (ResourceSurrogateId, SearchParamId, ResourceTypeId)
+ WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+CREATE UNIQUE INDEX IXU_ReferenceResourceId_SearchParamId_BaseUri_ResourceSurrogateId_ResourceTypeId
+ ON dbo.StringReferenceSearchParams (ReferenceResourceId, SearchParamId, BaseUri, ResourceSurrogateId, ResourceTypeId)
+ WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId (ResourceTypeId)
+GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Tables/Resource.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Tables/Resource.sql
deleted file mode 100644
index 9008e20cdf..0000000000
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Tables/Resource.sql
+++ /dev/null
@@ -1,74 +0,0 @@
--- Our code generator, that creates class wrappers on database objects, is not able to deal with views, but we stil want to refer to view objects in the code.
--- Workaround is to create table that looks like view, so code generator picks it up, and immediately drop it.
--- This would not be needed at all, if we followed different class generation strategy.
-CREATE TABLE dbo.CurrentResource -- This is replaced by view CurrentResource
-(
- ResourceTypeId smallint NOT NULL,
- ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL,
- Version int NOT NULL,
- IsHistory bit NOT NULL,
- ResourceSurrogateId bigint NOT NULL,
- IsDeleted bit NOT NULL,
- RequestMethod varchar(10) NULL,
- RawResource varbinary(max) NOT NULL,
- IsRawResourceMetaSet bit NOT NULL,
- SearchParamHash varchar(64) NULL,
- TransactionId bigint NULL,
- HistoryTransactionId bigint NULL
-)
-GO
-DROP TABLE dbo.CurrentResource
-GO
-CREATE TABLE dbo.Resource
-(
- ResourceTypeId smallint NOT NULL,
- ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL,
- Version int NOT NULL,
- IsHistory bit NOT NULL,
- ResourceSurrogateId bigint NOT NULL,
- IsDeleted bit NOT NULL,
- RequestMethod varchar(10) NULL,
- RawResource varbinary(max) NOT NULL,
- IsRawResourceMetaSet bit NOT NULL DEFAULT 0,
- SearchParamHash varchar(64) NULL,
- TransactionId bigint NULL, -- used for main CRUD operation
- HistoryTransactionId bigint NULL -- used by CRUD operation that moved resource version in invisible state
-
- CONSTRAINT PKC_Resource PRIMARY KEY CLUSTERED (ResourceTypeId, ResourceSurrogateId) WITH (DATA_COMPRESSION = PAGE) ON PartitionScheme_ResourceTypeId(ResourceTypeId),
- CONSTRAINT CH_Resource_RawResource_Length CHECK (RawResource > 0x0)
-)
-
-ALTER TABLE dbo.Resource SET ( LOCK_ESCALATION = AUTO )
-
-CREATE INDEX IX_ResourceTypeId_TransactionId ON dbo.Resource (ResourceTypeId, TransactionId) WHERE TransactionId IS NOT NULL ON PartitionScheme_ResourceTypeId (ResourceTypeId)
-CREATE INDEX IX_ResourceTypeId_HistoryTransactionId ON dbo.Resource (ResourceTypeId, HistoryTransactionId) WHERE HistoryTransactionId IS NOT NULL ON PartitionScheme_ResourceTypeId (ResourceTypeId)
-
-CREATE UNIQUE NONCLUSTERED INDEX IX_Resource_ResourceTypeId_ResourceId_Version ON dbo.Resource
-(
- ResourceTypeId,
- ResourceId,
- Version
-)
-ON PartitionScheme_ResourceTypeId(ResourceTypeId)
-
-CREATE UNIQUE NONCLUSTERED INDEX IX_Resource_ResourceTypeId_ResourceId ON dbo.Resource
-(
- ResourceTypeId,
- ResourceId
-)
-INCLUDE -- We want the query in UpsertResource, which is done with UPDLOCK AND HOLDLOCK, to not require a key lookup
-(
- Version,
- IsDeleted
-)
-WHERE IsHistory = 0
-ON PartitionScheme_ResourceTypeId(ResourceTypeId)
-
-CREATE UNIQUE NONCLUSTERED INDEX IX_Resource_ResourceTypeId_ResourceSurrgateId ON dbo.Resource
-(
- ResourceTypeId,
- ResourceSurrogateId
-)
-WHERE IsHistory = 0 AND IsDeleted = 0
-ON PartitionScheme_ResourceTypeId(ResourceTypeId)
-GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Types/ReferenceSearchParamList.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Types/ReferenceSearchParamList.sql
index 796604196e..2026574ad4 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Types/ReferenceSearchParamList.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Types/ReferenceSearchParamList.sql
@@ -7,7 +7,7 @@ CREATE TYPE dbo.ReferenceSearchParamList AS TABLE
,SearchParamId smallint NOT NULL
,BaseUri varchar(128) COLLATE Latin1_General_100_CS_AS NULL
,ReferenceResourceTypeId smallint NULL
- ,ReferenceResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL
+ ,ReferenceResourceId varchar(768) COLLATE Latin1_General_100_CS_AS NOT NULL
,ReferenceResourceVersion int NULL
UNIQUE (ResourceTypeId, ResourceSurrogateId, SearchParamId, BaseUri, ReferenceResourceTypeId, ReferenceResourceId)
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Types/ResourceListLake.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Types/ResourceListLake.sql
new file mode 100644
index 0000000000..054f3c9c82
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Types/ResourceListLake.sql
@@ -0,0 +1,23 @@
+--DROP TYPE dbo.ResourceListLake
+GO
+CREATE TYPE dbo.ResourceListLake AS TABLE
+(
+ ResourceTypeId smallint NOT NULL
+ ,ResourceSurrogateId bigint NOT NULL
+ ,ResourceId varchar(64) COLLATE Latin1_General_100_CS_AS NOT NULL
+ ,Version int NOT NULL
+ ,HasVersionToCompare bit NOT NULL -- in case of multiple versions per resource indicates that row contains (existing version + 1) value
+ ,IsDeleted bit NOT NULL
+ ,IsHistory bit NOT NULL
+ ,KeepHistory bit NOT NULL
+ ,RawResource varbinary(max) NULL
+ ,IsRawResourceMetaSet bit NOT NULL
+ ,RequestMethod varchar(10) NULL
+ ,SearchParamHash varchar(64) NULL
+ ,FileId bigint NULL
+ ,OffsetInFile int NULL
+
+ PRIMARY KEY (ResourceTypeId, ResourceSurrogateId)
+ ,UNIQUE (ResourceTypeId, ResourceId, Version)
+)
+GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Views/CurrentResource.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Views/CurrentResource.sql
index d4959610f0..4005d99558 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Views/CurrentResource.sql
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Views/CurrentResource.sql
@@ -1,6 +1,21 @@
CREATE VIEW dbo.CurrentResource
AS
-SELECT *
- FROM dbo.Resource
- WHERE IsHistory = 0
+SELECT A.ResourceTypeId
+ ,A.ResourceSurrogateId
+ ,ResourceId
+ ,A.ResourceIdInt
+ ,Version
+ ,IsHistory
+ ,IsDeleted
+ ,RequestMethod
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,TransactionId
+ ,HistoryTransactionId
+ ,FileId
+ ,OffsetInFile
+ FROM dbo.CurrentResources A
+ LEFT OUTER JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ LEFT OUTER JOIN dbo.ResourceIdIntMap C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = A.ResourceIdInt
GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Views/ReferenceSearchParam.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Views/ReferenceSearchParam.sql
new file mode 100644
index 0000000000..991a83b070
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Views/ReferenceSearchParam.sql
@@ -0,0 +1,23 @@
+CREATE VIEW dbo.ReferenceSearchParam
+AS
+SELECT A.ResourceTypeId
+ ,ResourceSurrogateId
+ ,SearchParamId
+ ,BaseUri
+ ,ReferenceResourceTypeId
+ ,ReferenceResourceId = B.ResourceId
+ ,ReferenceResourceIdInt
+ ,IsResourceRef
+ FROM dbo.ResourceReferenceSearchParams A
+ LEFT OUTER JOIN dbo.ResourceIdIntMap B ON B.ResourceTypeId = A.ReferenceResourceTypeId AND B.ResourceIdInt = A.ReferenceResourceIdInt
+UNION ALL
+SELECT ResourceTypeId
+ ,ResourceSurrogateId
+ ,SearchParamId
+ ,BaseUri
+ ,NULL
+ ,ReferenceResourceId
+ ,NULL
+ ,IsResourceRef
+ FROM dbo.StringReferenceSearchParams
+GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Views/Resource.sql b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Views/Resource.sql
new file mode 100644
index 0000000000..fd65f70e3c
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Views/Resource.sql
@@ -0,0 +1,160 @@
+CREATE VIEW dbo.Resource
+AS
+SELECT A.ResourceTypeId
+ ,A.ResourceSurrogateId
+ ,ResourceId
+ ,A.ResourceIdInt
+ ,Version
+ ,IsHistory
+ ,IsDeleted
+ ,RequestMethod
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,TransactionId
+ ,HistoryTransactionId
+ ,FileId
+ ,OffsetInFile
+ FROM dbo.CurrentResources A
+ LEFT OUTER JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ LEFT OUTER JOIN dbo.ResourceIdIntMap C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = A.ResourceIdInt
+UNION ALL
+SELECT A.ResourceTypeId
+ ,A.ResourceSurrogateId
+ ,ResourceId
+ ,A.ResourceIdInt
+ ,Version
+ ,IsHistory
+ ,IsDeleted
+ ,RequestMethod
+ ,RawResource
+ ,IsRawResourceMetaSet
+ ,SearchParamHash
+ ,TransactionId
+ ,HistoryTransactionId
+ ,FileId
+ ,OffsetInFile
+ FROM dbo.HistoryResources A
+ LEFT OUTER JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ LEFT OUTER JOIN dbo.ResourceIdIntMap C ON C.ResourceTypeId = A.ResourceTypeId AND C.ResourceIdInt = A.ResourceIdInt
+GO
+CREATE TRIGGER dbo.ResourceIns ON dbo.Resource INSTEAD OF INSERT
+AS
+BEGIN
+ INSERT INTO dbo.RawResources
+ ( ResourceTypeId, ResourceSurrogateId, RawResource )
+ SELECT ResourceTypeId, ResourceSurrogateId, RawResource
+ FROM Inserted
+ WHERE RawResource IS NOT NULL
+
+ INSERT INTO dbo.CurrentResources
+ ( ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile )
+ SELECT ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile
+ FROM Inserted
+ WHERE IsHistory = 0
+
+ INSERT INTO dbo.HistoryResources
+ ( ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile )
+ SELECT ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile
+ FROM Inserted
+ WHERE IsHistory = 1
+END
+GO
+CREATE TRIGGER dbo.ResourceUpd ON dbo.Resource INSTEAD OF UPDATE
+AS
+BEGIN
+ IF UPDATE(IsDeleted) AND UPDATE(RawResource) AND UPDATE(SearchParamHash) AND UPDATE(HistoryTransactionId) AND NOT UPDATE(IsHistory) -- hard delete resource
+ BEGIN
+ UPDATE B
+ SET RawResource = A.RawResource
+ FROM Inserted A
+ JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ IF @@rowcount = 0
+ INSERT INTO dbo.RawResources
+ ( ResourceTypeId, ResourceSurrogateId, RawResource )
+ SELECT ResourceTypeId, ResourceSurrogateId, RawResource
+ FROM Inserted
+ WHERE RawResource IS NOT NULL
+
+ UPDATE B
+ SET IsDeleted = A.IsDeleted
+ ,SearchParamHash = A.SearchParamHash
+ ,HistoryTransactionId = A.HistoryTransactionId
+ FROM Inserted A
+ JOIN dbo.CurrentResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ RETURN
+ END
+
+ IF UPDATE(SearchParamHash) AND NOT UPDATE(IsHistory) -- reindex
+ BEGIN
+ UPDATE B
+ SET SearchParamHash = A.SearchParamHash
+ FROM Inserted A
+ JOIN dbo.CurrentResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+ WHERE A.IsHistory = 0
+
+ RETURN
+ END
+
+ IF UPDATE(TransactionId) AND NOT UPDATE(IsHistory) -- cleanup trans
+ BEGIN
+ UPDATE B
+ SET TransactionId = A.TransactionId
+ FROM Inserted A
+ JOIN dbo.CurrentResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 0
+
+ UPDATE B
+ SET TransactionId = A.TransactionId
+ FROM Inserted A
+ JOIN dbo.HistoryResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 1
+
+ RETURN
+ END
+
+ IF UPDATE(RawResource) -- invisible records
+ BEGIN
+ UPDATE B
+ SET RawResource = A.RawResource
+ FROM Inserted A
+ JOIN dbo.RawResources B ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId
+
+ IF @@rowcount = 0
+ INSERT INTO dbo.RawResources
+ ( ResourceTypeId, ResourceSurrogateId, RawResource )
+ SELECT ResourceTypeId, ResourceSurrogateId, RawResource
+ FROM Inserted
+ WHERE RawResource IS NOT NULL
+ END
+
+ IF NOT UPDATE(IsHistory)
+ RAISERROR('Generic updates are not supported via Resource view',18,127)
+
+ DELETE FROM A
+ FROM dbo.CurrentResources A
+ WHERE EXISTS (SELECT * FROM Inserted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 1)
+
+ INSERT INTO dbo.HistoryResources
+ ( ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile )
+ SELECT ResourceTypeId, ResourceSurrogateId, ResourceIdInt, Version, IsDeleted, RequestMethod, IsRawResourceMetaSet, SearchParamHash, TransactionId, HistoryTransactionId, FileId, OffsetInFile
+ FROM Inserted
+ WHERE IsHistory = 1
+END
+GO
+CREATE TRIGGER dbo.ResourceDel ON dbo.Resource INSTEAD OF DELETE
+AS
+BEGIN
+ DELETE FROM A
+ FROM dbo.CurrentResources A
+ WHERE EXISTS (SELECT * FROM Deleted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 0)
+
+ DELETE FROM A
+ FROM dbo.HistoryResources A
+ WHERE EXISTS (SELECT * FROM Deleted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId AND B.IsHistory = 1)
+
+ DELETE FROM A
+ FROM dbo.RawResources A
+ WHERE EXISTS (SELECT * FROM Deleted B WHERE B.ResourceTypeId = A.ResourceTypeId AND B.ResourceSurrogateId = A.ResourceSurrogateId)
+END
+GO
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Search/Expressions/Visitors/QueryGenerators/SqlQueryGenerator.cs b/src/Microsoft.Health.Fhir.SqlServer/Features/Search/Expressions/Visitors/QueryGenerators/SqlQueryGenerator.cs
index 61695e6567..811b1986f8 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Search/Expressions/Visitors/QueryGenerators/SqlQueryGenerator.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Search/Expressions/Visitors/QueryGenerators/SqlQueryGenerator.cs
@@ -202,13 +202,16 @@ public override object VisitSqlRoot(SqlRootExpression expression, SearchOptions
StringBuilder.Append(VLatest.Resource.IsRawResourceMetaSet, resourceTableAlias).Append(", ");
- if (_schemaInfo.Current >= SchemaVersionConstants.SearchParameterHashSchemaVersion)
- {
- StringBuilder.Append(VLatest.Resource.SearchParamHash, resourceTableAlias).Append(", ");
- }
+ StringBuilder.Append(VLatest.Resource.SearchParamHash, resourceTableAlias).Append(", ");
StringBuilder.Append(VLatest.Resource.RawResource, resourceTableAlias);
+ if (_schemaInfo.Current >= SchemaVersionConstants.Lake)
+ {
+ StringBuilder.Append(", ").Append(VLatest.Resource.FileId, resourceTableAlias);
+ StringBuilder.Append(", ").Append(VLatest.Resource.OffsetInFile, resourceTableAlias);
+ }
+
if (IsSortValueNeeded(context))
{
StringBuilder.Append(", ").Append(TableExpressionName(_tableExpressionCounter)).Append(".SortValue");
@@ -271,15 +274,15 @@ public override object VisitSqlRoot(SqlRootExpression expression, SearchOptions
if (IsPrimaryKeySort(searchOptions))
{
StringBuilder.AppendDelimited(", ", searchOptions.Sort, (sb, sort) =>
- {
- Column column = sort.searchParameterInfo.Name switch
{
- SearchParameterNames.ResourceType => VLatest.Resource.ResourceTypeId,
- SearchParameterNames.LastUpdated => VLatest.Resource.ResourceSurrogateId,
- _ => throw new InvalidOperationException($"Unexpected sort parameter {sort.searchParameterInfo.Name}"),
- };
- sb.Append(column, resourceTableAlias).Append(" ").Append(sort.sortOrder == SortOrder.Ascending ? "ASC" : "DESC");
- })
+ Column column = sort.searchParameterInfo.Name switch
+ {
+ SearchParameterNames.ResourceType => VLatest.Resource.ResourceTypeId,
+ SearchParameterNames.LastUpdated => VLatest.Resource.ResourceSurrogateId,
+ _ => throw new InvalidOperationException($"Unexpected sort parameter {sort.searchParameterInfo.Name}"),
+ };
+ sb.Append(column, resourceTableAlias).Append(" ").Append(sort.sortOrder == SortOrder.Ascending ? "ASC" : "DESC");
+ })
.AppendLine();
}
else if (IsSortValueNeeded(searchOptions))
@@ -308,7 +311,7 @@ public override object VisitSqlRoot(SqlRootExpression expression, SearchOptions
return null;
}
- // TODO: Remove when code starts using TokenSearchParamHighCard table
+ // TODO: Remove. This is not needed as we use precise statistics.
private void AddOptionClause()
{
// if we have a complex query more than one SearchParemter, one of the parameters is "identifier", and we have an include
@@ -700,8 +703,16 @@ private void HandleTableKindChain(
.Append(VLatest.Resource.ResourceSurrogateId, chainedExpression.Reversed && searchParamTableExpression.ChainLevel > 1 ? referenceSourceTableAlias : referenceTargetResourceTableAlias).Append(" AS ").AppendLine(chainedExpression.Reversed && searchParamTableExpression.ChainLevel == 1 ? "Sid1 " : "Sid2 ")
.Append("FROM ").Append(VLatest.ReferenceSearchParam).Append(' ').AppendLine(referenceSourceTableAlias)
.Append(_joinShift).Append("JOIN ").Append(context.AddCurrentClause && _allowCurrent ? VLatest.CurrentResource : VLatest.Resource).Append(' ').Append(referenceTargetResourceTableAlias)
- .Append(" ON ").Append(VLatest.ReferenceSearchParam.ReferenceResourceTypeId, referenceSourceTableAlias).Append(" = ").Append(VLatest.Resource.ResourceTypeId, referenceTargetResourceTableAlias)
- .Append(" AND ").Append(VLatest.ReferenceSearchParam.ReferenceResourceId, referenceSourceTableAlias).Append(" = ").AppendLine(VLatest.Resource.ResourceId, referenceTargetResourceTableAlias);
+ .Append(" ON ").Append(VLatest.ReferenceSearchParam.ReferenceResourceTypeId, referenceSourceTableAlias).Append(" = ").Append(VLatest.Resource.ResourceTypeId, referenceTargetResourceTableAlias);
+ if (_schemaInfo.Current >= SchemaVersionConstants.Lake)
+ {
+ StringBuilder.Append(" AND ").Append(VLatest.ReferenceSearchParam.ReferenceResourceIdInt, referenceSourceTableAlias).Append(" = ").Append(VLatest.CurrentResources.ResourceIdInt, referenceTargetResourceTableAlias)
+ .Append(" AND ").Append(VLatest.ReferenceSearchParam.IsResourceRef, referenceSourceTableAlias).AppendLine(" = 1");
+ }
+ else
+ {
+ StringBuilder.Append(" AND ").Append(VLatest.ReferenceSearchParam.ReferenceResourceId, referenceSourceTableAlias).Append(" = ").AppendLine(VLatest.Resource.ResourceId, referenceTargetResourceTableAlias);
+ }
// For reverse chaining, if there is a parameter on the _id search parameter, we need another join to get the resource ID of the reference source (all we have is the surrogate ID at this point)
bool expressionOnTargetHandledBySecondJoin = chainedExpression.ExpressionOnTarget != null && chainedExpression.Reversed && chainedExpression.ExpressionOnTarget.AcceptVisitor(ExpressionContainsParameterVisitor.Instance, SearchParameterNames.Id);
@@ -796,8 +807,16 @@ private void HandleTableKindInclude(
StringBuilder.Append("FROM ").Append(VLatest.ReferenceSearchParam).Append(' ').AppendLine(referenceSourceTableAlias)
.Append(_joinShift).Append("JOIN ").Append(context.AddCurrentClause && _allowCurrent ? VLatest.CurrentResource : VLatest.Resource).Append(' ').Append(referenceTargetResourceTableAlias)
- .Append(" ON ").Append(VLatest.ReferenceSearchParam.ReferenceResourceTypeId, referenceSourceTableAlias).Append(" = ").Append(VLatest.Resource.ResourceTypeId, referenceTargetResourceTableAlias)
- .Append(" AND ").Append(VLatest.ReferenceSearchParam.ReferenceResourceId, referenceSourceTableAlias).Append(" = ").AppendLine(VLatest.Resource.ResourceId, referenceTargetResourceTableAlias);
+ .Append(" ON ").Append(VLatest.ReferenceSearchParam.ReferenceResourceTypeId, referenceSourceTableAlias).Append(" = ").Append(VLatest.Resource.ResourceTypeId, referenceTargetResourceTableAlias);
+ if (_schemaInfo.Current >= SchemaVersionConstants.Lake)
+ {
+ StringBuilder.Append(" AND ").Append(VLatest.ReferenceSearchParam.ReferenceResourceIdInt, referenceSourceTableAlias).Append(" = ").Append(VLatest.CurrentResources.ResourceIdInt, referenceTargetResourceTableAlias)
+ .Append(" AND ").Append(VLatest.ReferenceSearchParam.IsResourceRef, referenceSourceTableAlias).AppendLine(" = 1");
+ }
+ else
+ {
+ StringBuilder.Append(" AND ").Append(VLatest.ReferenceSearchParam.ReferenceResourceId, referenceSourceTableAlias).Append(" = ").AppendLine(VLatest.Resource.ResourceId, referenceTargetResourceTableAlias);
+ }
using (var delimited = StringBuilder.BeginDelimitedWhereClause())
{
@@ -1316,11 +1335,6 @@ private void AppendHistoryClause(in IndentedStringBuilder.DelimitedScope delimit
private void AppendMinOrMax(in IndentedStringBuilder.DelimitedScope delimited, SearchOptions context)
{
- if (_schemaInfo.Current < SchemaVersionConstants.AddMinMaxForDateAndStringSearchParamVersion)
- {
- return;
- }
-
delimited.BeginDelimitedElement();
if (context.Sort[0].sortOrder == SortOrder.Ascending)
{
@@ -1364,7 +1378,7 @@ private static bool IsPrimaryKeySort(SearchOptions searchOptions)
return searchOptions.Sort.All(s => s.searchParameterInfo.Name is SearchParameterNames.ResourceType or SearchParameterNames.LastUpdated);
}
- private bool IsSortValueNeeded(SearchOptions context)
+ internal bool IsSortValueNeeded(SearchOptions context)
{
if (context.Sort.Count == 0)
{
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Search/SqlServerSearchService.cs b/src/Microsoft.Health.Fhir.SqlServer/Features/Search/SqlServerSearchService.cs
index 5b61d12661..6bf1093c2c 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Search/SqlServerSearchService.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Search/SqlServerSearchService.cs
@@ -4,10 +4,12 @@
// -------------------------------------------------------------------------------------------------
using System;
+using System.ClientModel;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
+using System.Data.SqlTypes;
using System.Diagnostics;
using System.Globalization;
using System.IO;
@@ -43,6 +45,7 @@
using Microsoft.Health.SqlServer.Features.Schema;
using Microsoft.Health.SqlServer.Features.Schema.Model;
using Microsoft.Health.SqlServer.Features.Storage;
+using static System.Net.WebRequestMethods;
using SortOrder = Microsoft.Health.Fhir.Core.Features.Search.SortOrder;
namespace Microsoft.Health.Fhir.SqlServer.Features.Search
@@ -68,7 +71,6 @@ internal class SqlServerSearchService : SearchService
private readonly SchemaInformation _schemaInformation;
private readonly ICompressedRawResourceConverter _compressedRawResourceConverter;
private readonly RequestContextAccessor _requestContextAccessor;
- private const int _defaultNumberOfColumnsReadFromResult = 11;
private readonly SearchParameterInfo _fakeLastUpdate = new SearchParameterInfo(SearchParameterNames.LastUpdated, SearchParameterNames.LastUpdated);
private readonly IParameterStore _parameterStore;
private static ResourceSearchParamStats _resourceSearchParamStats;
@@ -336,6 +338,7 @@ await _sqlRetryService.ExecuteSql(
using (SqlCommand sqlCommand = connection.CreateCommand()) // WARNING, this code will not set sqlCommand.Transaction. Sql transactions via C#/.NET are not supported in this method.
{
sqlCommand.CommandTimeout = (int)_sqlServerDataStoreConfiguration.CommandTimeout.TotalSeconds;
+ var isSortValueNeeded = false;
var exportTimeTravel = clonedSearchOptions.QueryHints != null && ContainsGlobalEndSurrogateId(clonedSearchOptions);
if (exportTimeTravel)
@@ -358,6 +361,7 @@ await _sqlRetryService.ExecuteSql(
sqlException);
expression.AcceptVisitor(queryGenerator, clonedSearchOptions);
+ isSortValueNeeded = queryGenerator.IsSortValueNeeded(clonedSearchOptions);
SqlCommandSimplifier.RemoveRedundantParameters(stringBuilder, sqlCommand.Parameters, _logger);
@@ -381,6 +385,9 @@ await _sqlRetryService.ExecuteSql(
LogSqlCommand(sqlCommand);
+ ContinuationToken continuationToken = null;
+ var tmpResources = new List<(SearchResultEntry Entry, bool IsMetaSet, SqlBytes SqlBytes, long? FileId, int? OffsetInFile)>(sqlSearchOptions.MaxItemCount);
+ //// logic inside sql reader should be as short as possible
using (var reader = await sqlCommand.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken))
{
if (clonedSearchOptions.CountOnly)
@@ -407,7 +414,6 @@ await _sqlRetryService.ExecuteSql(
return;
}
- var resources = new List(sqlSearchOptions.MaxItemCount);
short? newContinuationType = null;
long? newContinuationId = null;
bool moreResults = false;
@@ -415,7 +421,6 @@ await _sqlRetryService.ExecuteSql(
string sortValue = null;
var isResultPartial = false;
- int numberOfColumnsRead = 0;
while (await reader.ReadAsync(cancellationToken))
{
@@ -431,7 +436,9 @@ await _sqlRetryService.ExecuteSql(
out bool isPartialEntry,
out bool isRawResourceMetaSet,
out string searchParameterHash,
- out byte[] rawResourceBytes,
+ out SqlBytes rawResourceSqlBytes,
+ out long? fileId,
+ out int? offsetInFile,
out bool isInvisible);
if (isInvisible)
@@ -439,8 +446,6 @@ await _sqlRetryService.ExecuteSql(
continue;
}
- numberOfColumnsRead = reader.FieldCount;
-
// If we get to this point, we know there are more results so we need a continuation token
// Additionally, this resource shouldn't be included in the results
if (matchCount >= clonedSearchOptions.MaxItemCount && isMatch)
@@ -450,37 +455,15 @@ await _sqlRetryService.ExecuteSql(
continue;
}
- Lazy rawResource = new Lazy(() => string.Empty);
-
- if (!clonedSearchOptions.OnlyIds)
- {
- rawResource = new Lazy(() =>
- {
- using var rawResourceStream = new MemoryStream(rawResourceBytes);
- var decompressedResource = _compressedRawResourceConverter.ReadCompressedRawResource(rawResourceStream);
-
- _logger.LogVerbose(_parameterStore, cancellationToken, "{NameOfResourceSurrogateId}: {ResourceSurrogateId}; {NameOfResourceTypeId}: {ResourceTypeId}; Decompressed length: {RawResourceLength}", nameof(resourceSurrogateId), resourceSurrogateId, nameof(resourceTypeId), resourceTypeId, decompressedResource.Length);
-
- if (string.IsNullOrEmpty(decompressedResource))
- {
- decompressedResource = MissingResourceFactory.CreateJson(resourceId, _model.GetResourceTypeName(resourceTypeId), "warning", "incomplete");
- _requestContextAccessor.SetMissingResourceCode(System.Net.HttpStatusCode.PartialContent);
- }
-
- return decompressedResource;
- });
- }
-
// See if this resource is a continuation token candidate and increase the count
if (isMatch)
{
newContinuationType = resourceTypeId;
newContinuationId = resourceSurrogateId;
- // For normal queries, we select _defaultNumberOfColumnsReadFromResult number of columns.
- // If we have more, that means we have an extra column tracking sort value.
+ // If sort value needed, that means we have an extra column tracking sort value.
// Keep track of sort value if this is the last row.
- if (matchCount == clonedSearchOptions.MaxItemCount - 1 && reader.FieldCount > _defaultNumberOfColumnsReadFromResult)
+ if (matchCount == clonedSearchOptions.MaxItemCount - 1 && isSortValueNeeded)
{
var tempSortValue = reader.GetValue(SortValueColumnName);
if ((tempSortValue as DateTime?) != null)
@@ -500,27 +483,31 @@ await _sqlRetryService.ExecuteSql(
// should be marked as partial
isResultPartial = isResultPartial || isPartialEntry;
- resources.Add(new SearchResultEntry(
- new ResourceWrapper(
- resourceId,
- version.ToString(CultureInfo.InvariantCulture),
- _model.GetResourceTypeName(resourceTypeId),
- clonedSearchOptions.OnlyIds ? null : new RawResource(rawResource, FhirResourceFormat.Json, isMetaSet: isRawResourceMetaSet),
- new ResourceRequest(requestMethod),
- resourceSurrogateId.ToLastUpdated(),
- isDeleted,
- null,
- null,
- null,
- searchParameterHash,
- resourceSurrogateId),
- isMatch ? SearchEntryMode.Match : SearchEntryMode.Include));
+ tmpResources.Add((new SearchResultEntry(
+ new ResourceWrapper(
+ resourceId,
+ version.ToString(CultureInfo.InvariantCulture),
+ _model.GetResourceTypeName(resourceTypeId),
+ null,
+ new ResourceRequest(requestMethod),
+ resourceSurrogateId.ToLastUpdated(),
+ isDeleted,
+ null,
+ null,
+ null,
+ searchParameterHash,
+ resourceSurrogateId),
+ isMatch ? SearchEntryMode.Match : SearchEntryMode.Include),
+ isRawResourceMetaSet,
+ rawResourceSqlBytes,
+ fileId,
+ offsetInFile));
}
// call NextResultAsync to get the info messages
await reader.NextResultAsync(cancellationToken);
- ContinuationToken continuationToken = moreResults
+ continuationToken = moreResults
? new ContinuationToken(
clonedSearchOptions.Sort.Select(s =>
s.searchParameterInfo.Name switch
@@ -547,7 +534,7 @@ await _sqlRetryService.ExecuteSql(
clonedSearchOptions.Sort[0].searchParameterInfo.Code != KnownQueryParameterNames.LastUpdated)
{
// If there is an extra column for sort value, we know we have searched for sort values. If no results were returned, we don't know if we have searched for sort values so we need to assume we did so we run the second phase.
- sqlSearchOptions.DidWeSearchForSortValue = numberOfColumnsRead > _defaultNumberOfColumnsReadFromResult;
+ sqlSearchOptions.DidWeSearchForSortValue = isSortValueNeeded;
}
// This value is set inside the SortRewriter. If it is set, we need to pass
@@ -561,9 +548,34 @@ await _sqlRetryService.ExecuteSql(
{
sqlSearchOptions.SortHasMissingModifier = true;
}
+ }
+
+ // add raw resource to search entry
+ var resources = new List(sqlSearchOptions.MaxItemCount);
+ foreach (var tmpResource in tmpResources)
+ {
+ if (!clonedSearchOptions.OnlyIds)
+ {
+ var rawResource = new Lazy(() =>
+ {
+ var decompressed = SqlStoreClient.ReadCompressedRawResource(tmpResource.SqlBytes, _compressedRawResourceConverter.ReadCompressedRawResource);
+ _logger.LogVerbose(_parameterStore, cancellationToken, "{NameOfResourceSurrogateId}: {ResourceSurrogateId}; {NameOfResourceTypeId}: {ResourceTypeId}; Decompressed length: {RawResourceLength}", nameof(tmpResource.Entry.Resource.ResourceSurrogateId), tmpResource.Entry.Resource.ResourceSurrogateId, nameof(tmpResource.Entry.Resource.ResourceTypeName), tmpResource.Entry.Resource.ResourceTypeName, decompressed.Length);
+ if (string.IsNullOrEmpty(decompressed))
+ {
+ decompressed = MissingResourceFactory.CreateJson(tmpResource.Entry.Resource.ResourceId, tmpResource.Entry.Resource.ResourceTypeName, "warning", "incomplete");
+ _requestContextAccessor.SetMissingResourceCode(System.Net.HttpStatusCode.PartialContent);
+ }
+
+ return decompressed;
+ });
- searchResult = new SearchResult(resources, continuationToken?.ToJson(), originalSort, clonedSearchOptions.UnsupportedSearchParams);
+ tmpResource.Entry.Resource.RawResource = new RawResource(rawResource, FhirResourceFormat.Json, isMetaSet: tmpResource.IsMetaSet);
+ }
+
+ resources.Add(tmpResource.Entry);
}
+
+ searchResult = new SearchResult(resources, continuationToken?.ToJson(), originalSort, clonedSearchOptions.UnsupportedSearchParams);
}
},
_logger,
@@ -624,13 +636,13 @@ public async Task SearchBySurrogateIdRange(string resourceType, lo
sqlCommand.CommandTimeout = GetReindexCommandTimeout();
PopulateSqlCommandFromQueryHints(sqlCommand, resourceTypeId, startId, endId, windowEndId, includeHistory, includeDeleted);
LogSqlCommand(sqlCommand);
- List resources = null;
+ List<(SearchResultEntry Entry, SqlBytes SqlBytes, long? FileId, int? OffsetInFile, bool IsMetaSet, string ResourceId)> resources = null;
await _sqlRetryService.ExecuteSql(
sqlCommand,
async (cmd, cancel) =>
{
using SqlDataReader reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancel);
- resources = new List();
+ resources = new List<(SearchResultEntry Entry, SqlBytes SqlBytes, long? FileId, int? OffsetInFile, bool IsMetaSet, string ResourceId)>();
while (await reader.ReadAsync(cancel))
{
ReadWrapper(
@@ -645,7 +657,9 @@ await _sqlRetryService.ExecuteSql(
out bool isPartialEntry,
out bool isRawResourceMetaSet,
out string searchParameterHash,
- out byte[] rawResourceBytes,
+ out SqlBytes rawResourceSqlBytes,
+ out long? fileId,
+ out int? offsetInFile,
out bool isInvisible);
if (isInvisible)
@@ -659,30 +673,27 @@ await _sqlRetryService.ExecuteSql(
continue;
}
- using var rawResourceStream = new MemoryStream(rawResourceBytes);
- var rawResource = _compressedRawResourceConverter.ReadCompressedRawResource(rawResourceStream);
-
- if (string.IsNullOrEmpty(rawResource))
- {
- rawResource = MissingResourceFactory.CreateJson(resourceId, _model.GetResourceTypeName(resourceTypeId), "warning", "incomplete");
- _requestContextAccessor.SetMissingResourceCode(System.Net.HttpStatusCode.PartialContent);
- }
-
- resources.Add(new SearchResultEntry(
- new ResourceWrapper(
- resourceId,
- version.ToString(CultureInfo.InvariantCulture),
- resourceType,
- new RawResource(rawResource, FhirResourceFormat.Json, isMetaSet: isRawResourceMetaSet),
- new ResourceRequest(requestMethod),
- resourceSurrogateId.ToLastUpdated(),
- isDeleted,
- null,
- null,
- null,
- searchParameterHash,
- resourceSurrogateId),
- isMatch ? SearchEntryMode.Match : SearchEntryMode.Include));
+ resources.Add(
+ (new SearchResultEntry(
+ new ResourceWrapper(
+ resourceId,
+ version.ToString(CultureInfo.InvariantCulture),
+ resourceType,
+ null,
+ new ResourceRequest(requestMethod),
+ resourceSurrogateId.ToLastUpdated(),
+ isDeleted,
+ null,
+ null,
+ null,
+ searchParameterHash,
+ resourceSurrogateId),
+ isMatch ? SearchEntryMode.Match : SearchEntryMode.Include),
+ rawResourceSqlBytes,
+ fileId,
+ offsetInFile,
+ isRawResourceMetaSet,
+ resourceId));
}
return;
@@ -690,7 +701,20 @@ await _sqlRetryService.ExecuteSql(
_logger,
null,
cancellationToken);
- return new SearchResult(resources, null, null, new List>()) { TotalCount = resources.Count };
+ foreach (var resource in resources)
+ {
+ var rawResource = SqlStoreClient.ReadCompressedRawResource(resource.SqlBytes, _compressedRawResourceConverter.ReadCompressedRawResource);
+
+ if (string.IsNullOrEmpty(rawResource))
+ {
+ rawResource = MissingResourceFactory.CreateJson(resource.ResourceId, _model.GetResourceTypeName(resourceTypeId), "warning", "incomplete");
+ _requestContextAccessor.SetMissingResourceCode(System.Net.HttpStatusCode.PartialContent);
+ }
+
+ resource.Entry.Resource.RawResource = new RawResource(rawResource, FhirResourceFormat.Json, resource.IsMetaSet);
+ }
+
+ return new SearchResult(resources.Select(_ => _.Entry), null, null, new List>()) { TotalCount = resources.Count };
}
private static (long StartId, long EndId) ReaderToSurrogateIdRange(SqlDataReader sqlDataReader)
@@ -832,7 +856,9 @@ private void ReadWrapper(
out bool isPartialEntry,
out bool isRawResourceMetaSet,
out string searchParameterHash,
- out byte[] rawResourceBytes,
+ out SqlBytes rawResourceSqlBytes,
+ out long? fileId,
+ out int? offsetInFile,
out bool isInvisible)
{
resourceTypeId = reader.Read(VLatest.Resource.ResourceTypeId, 0);
@@ -845,8 +871,20 @@ private void ReadWrapper(
isPartialEntry = reader.Read(_isPartial, 7);
isRawResourceMetaSet = reader.Read(VLatest.Resource.IsRawResourceMetaSet, 8);
searchParameterHash = reader.Read(VLatest.Resource.SearchParamHash, 9);
- rawResourceBytes = reader.GetSqlBytes(10).Value;
- isInvisible = rawResourceBytes.Length == 1 && rawResourceBytes[0] == 0xF;
+ rawResourceSqlBytes = reader.GetSqlBytes(10);
+ //// TODO: Remove field count check when Lake schema is deployed
+ //// Number of fields in old schema is either 11 or 12 (12th is sort value). In new schema, it is either 13 or 14.
+ fileId = reader.FieldCount > 12 ? reader.Read(VLatest.Resource.FileId, 11) : null;
+ offsetInFile = reader.FieldCount > 12 ? reader.Read(VLatest.Resource.OffsetInFile, 12) : null;
+ isInvisible = false;
+ if (!rawResourceSqlBytes.IsNull)
+ {
+ var rawResourceBytes = rawResourceSqlBytes.Value;
+ if (rawResourceBytes.Length == 1 && rawResourceBytes[0] == 0xF)
+ {
+ isInvisible = true;
+ }
+ }
}
[Conditional("DEBUG")]
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlRetry/ISqlRetryService.cs b/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlRetry/ISqlRetryService.cs
index c165b32568..8a2a0d7e46 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlRetry/ISqlRetryService.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlRetry/ISqlRetryService.cs
@@ -14,6 +14,8 @@ namespace Microsoft.Health.Fhir.SqlServer.Features.Storage
{
public interface ISqlRetryService
{
+ string Database { get; }
+
Task TryLogEvent(string process, string status, string text, DateTime? startDate, CancellationToken cancellationToken);
Task ExecuteSql(Func action, ILogger logger, CancellationToken cancellationToken, bool isReadOnly = false);
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlRetry/SqlRetryService.cs b/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlRetry/SqlRetryService.cs
index ed907b5e68..0014b61bf3 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlRetry/SqlRetryService.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlRetry/SqlRetryService.cs
@@ -74,6 +74,7 @@ private readonly HashSet _transientErrors
private static object _initLocker = new object();
private static EventLogHandler _eventLogHandler;
private CoreFeatureConfiguration _coreFeatureConfiguration;
+ private readonly string _database;
///
/// Constructor that initializes this implementation of the ISqlRetryService interface. This class
@@ -99,6 +100,13 @@ public SqlRetryService(
_commandTimeout = (int)EnsureArg.IsNotNull(sqlServerDataStoreConfiguration?.Value, nameof(sqlServerDataStoreConfiguration)).CommandTimeout.TotalSeconds;
_sqlConnectionBuilder = sqlConnectionBuilder;
+ _database = sqlConnectionBuilder.DefaultDatabase;
+ EnsureArg.IsNotNull(_database, "DefaultDatabase");
+ if (_database == "master")
+ {
+ throw new ArgumentException("default database cannot be master");
+ }
+
_coreFeatureConfiguration = coreFeatureConfiguration.Value;
if (sqlRetryServiceOptions.Value.RemoveTransientErrors != null)
@@ -138,6 +146,8 @@ private SqlRetryService(ISqlConnectionBuilder sqlConnectionBuilder)
///
public delegate bool IsExceptionRetriable(Exception ex);
+ public string Database => _database;
+
///
/// Simplified class generator.
///
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlServerFhirDataStore.cs b/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlServerFhirDataStore.cs
index b604769d3b..1df13f9f6e 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlServerFhirDataStore.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlServerFhirDataStore.cs
@@ -6,11 +6,15 @@
using System;
using System.Collections.Generic;
using System.Data;
+using System.Diagnostics;
using System.Globalization;
+using System.IO;
using System.Linq;
using System.Security.Cryptography;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Azure.Storage.Blobs.Specialized;
using EnsureThat;
using Hl7.FhirPath.Sprache;
using Microsoft.Data.SqlClient;
@@ -26,6 +30,7 @@
using Microsoft.Health.Fhir.Core.Features.Persistence;
using Microsoft.Health.Fhir.Core.Features.Persistence.Orchestration;
using Microsoft.Health.Fhir.Core.Models;
+using Microsoft.Health.Fhir.SqlServer.Features.Schema;
using Microsoft.Health.Fhir.SqlServer.Features.Schema.Model;
using Microsoft.Health.Fhir.SqlServer.Features.Storage.TvpRowGeneration;
using Microsoft.Health.Fhir.SqlServer.Features.Storage.TvpRowGeneration.Merge;
@@ -380,7 +385,9 @@ private async Task> ImportResourcesAsync(IReadOnlyList 3 ? 10 : 1000, cancellationToken); // if >3 assume that it is id generation problem
+ await _sqlRetryService.TryLogEvent(nameof(ImportResourcesInternalAsync), "Warn", $"retries={retries} resources={resources.Count} error={sqlEx.Message}", null, cancellationToken);
+ await Task.Delay(retries > 3 ? 10 : 500, cancellationToken); // if >3 assume that it is id generation problem
continue;
}
@@ -699,7 +707,15 @@ internal async Task MergeResourcesWrapperAsync(long transactionId, bool singleTr
cmd.Parameters.AddWithValue("@IsResourceChangeCaptureEnabled", _coreFeatures.SupportsResourceChangeCapture);
cmd.Parameters.AddWithValue("@TransactionId", transactionId);
cmd.Parameters.AddWithValue("@SingleTransaction", singleTransaction);
- new ResourceListTableValuedParameterDefinition("@Resources").AddParameter(cmd.Parameters, new ResourceListRowGenerator(_model, _compressedRawResourceConverter).GenerateRows(mergeWrappers));
+ if (_schemaInformation.Current >= SchemaVersionConstants.Lake)
+ {
+ new ResourceListLakeTableValuedParameterDefinition("@ResourcesLake").AddParameter(cmd.Parameters, new ResourceListLakeRowGenerator(_model, _compressedRawResourceConverter).GenerateRows(mergeWrappers));
+ }
+ else
+ {
+ new ResourceListTableValuedParameterDefinition("@Resources").AddParameter(cmd.Parameters, new ResourceListRowGenerator(_model, _compressedRawResourceConverter).GenerateRows(mergeWrappers));
+ }
+
new ResourceWriteClaimListTableValuedParameterDefinition("@ResourceWriteClaims").AddParameter(cmd.Parameters, new ResourceWriteClaimListRowGenerator(_model, _searchParameterTypeMap).GenerateRows(mergeWrappers));
new ReferenceSearchParamListTableValuedParameterDefinition("@ReferenceSearchParams").AddParameter(cmd.Parameters, new ReferenceSearchParamListRowGenerator(_model, _searchParameterTypeMap).GenerateRows(mergeWrappers));
new TokenSearchParamListTableValuedParameterDefinition("@TokenSearchParams").AddParameter(cmd.Parameters, new TokenSearchParamListRowGenerator(_model, _searchParameterTypeMap).GenerateRows(mergeWrappers));
@@ -765,7 +781,8 @@ public async Task GetAsync(ResourceKey key, CancellationToken c
public async Task HardDeleteAsync(ResourceKey key, bool keepCurrentVersion, bool allowPartialSuccess, CancellationToken cancellationToken)
{
- await _sqlStoreClient.HardDeleteAsync(_model.GetResourceTypeId(key.ResourceType), key.Id, keepCurrentVersion, _coreFeatures.SupportsResourceChangeCapture, cancellationToken);
+ var makeResourceInvisible = _coreFeatures.SupportsResourceChangeCapture;
+ await _sqlStoreClient.HardDeleteAsync(_model.GetResourceTypeId(key.ResourceType), key.Id, keepCurrentVersion, makeResourceInvisible, cancellationToken);
}
public async Task BulkUpdateSearchParameterIndicesAsync(IReadOnlyCollection resources, CancellationToken cancellationToken)
@@ -777,7 +794,15 @@ public async Task BulkUpdateSearchParameterIndicesAsync(IReadOnlyCollection new MergeResourceWrapper(_, false, false)).ToList();
using var cmd = new SqlCommand("dbo.UpdateResourceSearchParams") { CommandType = CommandType.StoredProcedure, CommandTimeout = 300 + (int)(3600.0 / 10000 * mergeWrappers.Count) };
- new ResourceListTableValuedParameterDefinition("@Resources").AddParameter(cmd.Parameters, new ResourceListRowGenerator(_model, _compressedRawResourceConverter).GenerateRows(mergeWrappers));
+ if (_schemaInformation.Current >= SchemaVersionConstants.Lake)
+ {
+ new ResourceListLakeTableValuedParameterDefinition("@ResourcesLake").AddParameter(cmd.Parameters, new ResourceListLakeRowGenerator(_model, _compressedRawResourceConverter).GenerateRows(mergeWrappers));
+ }
+ else
+ {
+ new ResourceListTableValuedParameterDefinition("@Resources").AddParameter(cmd.Parameters, new ResourceListRowGenerator(_model, _compressedRawResourceConverter).GenerateRows(mergeWrappers));
+ }
+
new ResourceWriteClaimListTableValuedParameterDefinition("@ResourceWriteClaims").AddParameter(cmd.Parameters, new ResourceWriteClaimListRowGenerator(_model, _searchParameterTypeMap).GenerateRows(mergeWrappers));
new ReferenceSearchParamListTableValuedParameterDefinition("@ReferenceSearchParams").AddParameter(cmd.Parameters, new ReferenceSearchParamListRowGenerator(_model, _searchParameterTypeMap).GenerateRows(mergeWrappers));
new TokenSearchParamListTableValuedParameterDefinition("@TokenSearchParams").AddParameter(cmd.Parameters, new TokenSearchParamListRowGenerator(_model, _searchParameterTypeMap).GenerateRows(mergeWrappers));
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlStoreClient.cs b/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlStoreClient.cs
index f6b27e3848..422c264c54 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlStoreClient.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlStoreClient.cs
@@ -6,17 +6,21 @@
using System;
using System.Collections.Generic;
using System.Data;
+using System.Data.SqlTypes;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Azure.Storage.Blobs;
using EnsureThat;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Logging;
+using Microsoft.Health.Core.Features.Context;
using Microsoft.Health.Fhir.Core.Features.Persistence;
using Microsoft.Health.Fhir.Core.Models;
using Microsoft.Health.Fhir.SqlServer.Features.Schema.Model;
+using Microsoft.Health.Fhir.SqlServer.Features.Search;
using Microsoft.Health.SqlServer.Features.Storage;
using Task = System.Threading.Tasks.Task;
@@ -29,7 +33,7 @@ internal class SqlStoreClient
{
private readonly ISqlRetryService _sqlRetryService;
private readonly ILogger _logger;
- private const string _invisibleResource = " ";
+ internal const string InvisibleResource = " ";
public SqlStoreClient(ISqlRetryService sqlRetryService, ILogger logger)
{
@@ -37,13 +41,13 @@ public SqlStoreClient(ISqlRetryService sqlRetryService, ILogger
_logger = EnsureArg.IsNotNull(logger, nameof(logger));
}
- public async Task HardDeleteAsync(short resourceTypeId, string resourceId, bool keepCurrentVersion, bool isResourceChangeCaptureEnabled, CancellationToken cancellationToken)
+ public async Task HardDeleteAsync(short resourceTypeId, string resourceId, bool keepCurrentVersion, bool makeResourceInvisible, CancellationToken cancellationToken)
{
using var cmd = new SqlCommand() { CommandText = "dbo.HardDeleteResource", CommandType = CommandType.StoredProcedure };
cmd.Parameters.AddWithValue("@ResourceTypeId", resourceTypeId);
cmd.Parameters.AddWithValue("@ResourceId", resourceId);
cmd.Parameters.AddWithValue("@KeepCurrentVersion", keepCurrentVersion);
- cmd.Parameters.AddWithValue("@IsResourceChangeCaptureEnabled", isResourceChangeCaptureEnabled);
+ cmd.Parameters.AddWithValue("@MakeResourceInvisible", makeResourceInvisible);
await cmd.ExecuteNonQueryAsync(_sqlRetryService, _logger, cancellationToken);
}
@@ -73,7 +77,7 @@ public async Task> GetAsync(IReadOnlyList { return ReadResourceWrapper(reader, false, decompress, getResourceTypeName); }, _logger, cancellationToken, isReadOnly: isReadOnly)).Where(_ => includeInvisible || _.RawResource.Data != _invisibleResource).ToList();
+ return await ReadResourceWrappers(cmd, decompress, getResourceTypeName, isReadOnly, false, cancellationToken, includeInvisible);
}
catch (Exception e)
{
@@ -90,6 +94,29 @@ public async Task> GetAsync(IReadOnlyList> ReadResourceWrappers(SqlCommand cmd, Func decompress, Func getResourceTypeName, bool isReadOnly, bool readRequestMethod, CancellationToken cancellationToken, bool includeInvisible = false)
+ {
+ var wrappers = (await cmd.ExecuteReaderAsync(_sqlRetryService, (reader) => { return ReadTemporaryResourceWrapper(reader, readRequestMethod, getResourceTypeName); }, _logger, cancellationToken, isReadOnly: isReadOnly)).ToList();
+ return wrappers.Where(_ => includeInvisible || _.Wrapper.RawResource.Data != InvisibleResource).Select(_ => _.Wrapper).ToList();
+ }
+
+ internal static string ReadCompressedRawResource(SqlBytes bytes, Func decompress)
+ {
+ var rawResourceBytes = bytes.Value;
+ string rawResource;
+ if (rawResourceBytes.Length == 1 && rawResourceBytes[0] == 0xF) // invisible resource
+ {
+ rawResource = InvisibleResource;
+ }
+ else
+ {
+ using var rawResourceStream = new MemoryStream(rawResourceBytes);
+ rawResource = decompress(rawResourceStream);
+ }
+
+ return rawResource;
+ }
+
public async Task> GetResourceVersionsAsync(IReadOnlyList keys, Func decompress, CancellationToken cancellationToken)
{
if (keys == null || keys.Count == 0)
@@ -101,7 +128,7 @@ public async Task> GetAsync(IReadOnlyList new ResourceDateKeyListRow(_.ResourceTypeId, _.Id, _.ResourceSurrogateId));
new ResourceDateKeyListTableValuedParameterDefinition("@ResourceDateKeys").AddParameter(cmd.Parameters, tvpRows);
var table = VLatest.Resource;
- var resources = await cmd.ExecuteReaderAsync(
+ var tmpResources = await cmd.ExecuteReaderAsync(
_sqlRetryService,
(reader) =>
{
@@ -110,49 +137,43 @@ public async Task> GetAsync(IReadOnlyList 4 && version == 0)
+ SqlBytes matchedBytes = null;
+ long? matchedFileId = null;
+ int? matchedOffsetInFile = null;
+ if (version == 0) // there is a match
{
matchedVersion = reader.Read(table.Version, 4).ToString();
- matchedRawResource = new RawResource(ReadRawResource(reader, decompress, 5), FhirResourceFormat.Json, true);
+ matchedBytes = reader.GetSqlBytes(5);
+ matchedFileId = reader.FieldCount > 6 ? reader.Read(table.FileId, 6) : null; // TODO: Remove field count check after deployment
+ matchedOffsetInFile = reader.FieldCount > 6 ? reader.Read(table.OffsetInFile, 7) : null;
}
- return (new ResourceDateKey(resourceTypeId, resourceId, resourceSurrogateId, version.ToString(CultureInfo.InvariantCulture)), (matchedVersion, matchedRawResource));
+ return (new ResourceDateKey(resourceTypeId, resourceId, resourceSurrogateId, version.ToString(CultureInfo.InvariantCulture)), (matchedVersion, matchedBytes, matchedFileId, matchedOffsetInFile));
},
_logger,
cancellationToken);
- return resources;
- }
-
- private static Lazy ReadRawResource(SqlDataReader reader, Func decompress, int index)
- {
- var rawResourceBytes = reader.GetSqlBytes(index).Value;
- Lazy rawResource;
- if (rawResourceBytes.Length == 1 && rawResourceBytes[0] == 0xF) // invisible resource
+ var resources = tmpResources.Select(_ =>
{
- rawResource = new Lazy(_invisibleResource);
- }
- else
- {
- rawResource = new Lazy(() =>
+ var (key, (version, bytes, fileId, offsetInFile)) = _;
+ RawResource rawResource = null;
+ if (_.Item2.matchedVersion != null)
{
- using var rawResourceStream = new MemoryStream(rawResourceBytes);
- return decompress(rawResourceStream);
- });
- }
+ rawResource = new RawResource(ReadCompressedRawResource(bytes, decompress), FhirResourceFormat.Json, false);
+ }
- return rawResource;
+ return (key, (version, rawResource));
+ }).ToList();
+ return resources;
}
internal async Task> GetResourcesByTransactionIdAsync(long transactionId, Func decompress, Func getResourceTypeName, CancellationToken cancellationToken)
{
await using var cmd = new SqlCommand() { CommandText = "dbo.GetResourcesByTransactionId", CommandType = CommandType.StoredProcedure, CommandTimeout = 600 };
cmd.Parameters.AddWithValue("@TransactionId", transactionId);
- //// ignore invisible resources
- return (await cmd.ExecuteReaderAsync(_sqlRetryService, (reader) => { return ReadResourceWrapper(reader, true, decompress, getResourceTypeName); }, _logger, cancellationToken)).Where(_ => _.RawResource.Data != _invisibleResource).ToList();
+ return await ReadResourceWrappers(cmd, decompress, getResourceTypeName, false, true, cancellationToken, false);
}
- private static ResourceWrapper ReadResourceWrapper(SqlDataReader reader, bool readRequestMethod, Func decompress, Func getResourceTypeName)
+ private static (ResourceWrapper Wrapper, bool IsMetaSet, SqlBytes SqlBytes, long? FileId, int? OffsetInFile) ReadTemporaryResourceWrapper(SqlDataReader reader, bool readRequestMethod, Func getResourceTypeName)
{
var resourceTypeId = reader.Read(VLatest.Resource.ResourceTypeId, 0);
var resourceId = reader.Read(VLatest.Resource.ResourceId, 1);
@@ -160,15 +181,17 @@ private static ResourceWrapper ReadResourceWrapper(SqlDataReader reader, bool re
var version = reader.Read(VLatest.Resource.Version, 3);
var isDeleted = reader.Read(VLatest.Resource.IsDeleted, 4);
var isHistory = reader.Read(VLatest.Resource.IsHistory, 5);
- var rawResource = ReadRawResource(reader, decompress, 6);
+ var bytes = reader.GetSqlBytes(6);
+ var fileId = reader.FieldCount > 10 ? reader.Read(VLatest.Resource.FileId, readRequestMethod ? 10 : 9) : null; // TODO: Remove field count check after Lake schema deployment
+ var offsetInFile = reader.FieldCount > 10 ? reader.Read(VLatest.Resource.OffsetInFile, readRequestMethod ? 11 : 10) : null;
var isRawResourceMetaSet = reader.Read(VLatest.Resource.IsRawResourceMetaSet, 7);
var searchParamHash = reader.Read(VLatest.Resource.SearchParamHash, 8);
var requestMethod = readRequestMethod ? reader.Read(VLatest.Resource.RequestMethod, 9) : null;
- return new ResourceWrapper(
+ var wrapper = new ResourceWrapper(
resourceId,
version.ToString(CultureInfo.InvariantCulture),
getResourceTypeName(resourceTypeId),
- new RawResource(rawResource, FhirResourceFormat.Json, isMetaSet: isRawResourceMetaSet),
+ null,
readRequestMethod ? new ResourceRequest(requestMethod) : null,
resourceSurrogateId.ToLastUpdated(),
isDeleted,
@@ -180,6 +203,8 @@ private static ResourceWrapper ReadResourceWrapper(SqlDataReader reader, bool re
{
IsHistory = isHistory,
};
+
+ return (wrapper, isRawResourceMetaSet, bytes, fileId, offsetInFile);
}
internal async Task MergeResourcesPutTransactionHeartbeatAsync(long transactionId, TimeSpan heartbeatPeriod, CancellationToken cancellationToken)
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/TvpRowGeneration/Merge/MergeResourceWrapper.cs b/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/TvpRowGeneration/Merge/MergeResourceWrapper.cs
index 2984d5bdd3..b364e05141 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/TvpRowGeneration/Merge/MergeResourceWrapper.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/TvpRowGeneration/Merge/MergeResourceWrapper.cs
@@ -30,5 +30,15 @@ internal MergeResourceWrapper(ResourceWrapper resourceWrapper, bool keepHistory,
/// Flag indicating whether version in resource wrapper == (existing version in the database + 1)
///
public bool HasVersionToCompare { get; internal set; }
+
+ ///
+ /// Adls file Id
+ ///
+ public long? FileId { get; internal set; }
+
+ ///
+ /// Reasource record offset in adls file
+ ///
+ public int? OffsetInFile { get; internal set; }
}
}
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/TvpRowGeneration/Merge/ResourceListLakeRowGenerator.cs b/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/TvpRowGeneration/Merge/ResourceListLakeRowGenerator.cs
new file mode 100644
index 0000000000..3bb04cbb0d
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Storage/TvpRowGeneration/Merge/ResourceListLakeRowGenerator.cs
@@ -0,0 +1,41 @@
+// -------------------------------------------------------------------------------------------------
+// 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.Generic;
+using EnsureThat;
+using Microsoft.Health.Fhir.Core.Features.Operations;
+using Microsoft.Health.Fhir.SqlServer.Features.Schema.Model;
+using Microsoft.Health.Fhir.SqlServer.Features.Storage.TvpRowGeneration.Merge;
+using Microsoft.Health.SqlServer.Features.Schema.Model;
+using Microsoft.IO;
+
+namespace Microsoft.Health.Fhir.SqlServer.Features.Storage.TvpRowGeneration
+{
+ internal class ResourceListLakeRowGenerator : ITableValuedParameterRowGenerator, ResourceListLakeRow>
+ {
+ private readonly ISqlServerFhirModel _model;
+ private readonly ICompressedRawResourceConverter _compressedRawResourceConverter;
+ private readonly RecyclableMemoryStreamManager _memoryStreamManager;
+
+ public ResourceListLakeRowGenerator(ISqlServerFhirModel model, ICompressedRawResourceConverter compressedRawResourceConverter)
+ {
+ _model = EnsureArg.IsNotNull(model, nameof(model));
+ _compressedRawResourceConverter = EnsureArg.IsNotNull(compressedRawResourceConverter, nameof(compressedRawResourceConverter));
+ _memoryStreamManager = new RecyclableMemoryStreamManager();
+ }
+
+ public IEnumerable GenerateRows(IReadOnlyList mergeWrappers)
+ {
+ foreach (var merge in mergeWrappers)
+ {
+ var wrapper = merge.ResourceWrapper;
+ using var stream = new RecyclableMemoryStream(_memoryStreamManager, tag: nameof(ResourceListRowGenerator));
+ _compressedRawResourceConverter.WriteCompressedRawResource(stream, wrapper.RawResource.Data);
+ stream.Seek(0, 0);
+ yield return new ResourceListLakeRow(_model.GetResourceTypeId(wrapper.ResourceTypeName), merge.ResourceWrapper.ResourceSurrogateId, wrapper.ResourceId, int.Parse(wrapper.Version), merge.HasVersionToCompare, wrapper.IsDeleted, wrapper.IsHistory, merge.KeepHistory, merge.OffsetInFile.HasValue ? null : stream, wrapper.RawResource.IsMetaSet, wrapper.Request?.Method, wrapper.SearchParameterHash, merge.FileId, merge.OffsetInFile);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Watchdogs/InvisibleHistoryCleanupWatchdog.cs b/src/Microsoft.Health.Fhir.SqlServer/Features/Watchdogs/InvisibleHistoryCleanupWatchdog.cs
index 7efa16a6c7..1369046c22 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Features/Watchdogs/InvisibleHistoryCleanupWatchdog.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Watchdogs/InvisibleHistoryCleanupWatchdog.cs
@@ -51,8 +51,7 @@ protected override async Task RunWorkAsync(CancellationToken cancellationToken)
var visibility = await _store.MergeResourcesGetTransactionVisibilityAsync(cancellationToken);
_logger.LogInformation($"{Name}: last cleaned up transaction={lastTranId} visibility={visibility}.");
- IReadOnlyList<(long TransactionId, DateTime? VisibleDate, DateTime? InvisibleHistoryRemovedDate)> transToClean =
- await _store.GetTransactionsAsync(lastTranId, visibility, cancellationToken, DateTime.UtcNow.AddDays(-1 * RetentionPeriodDays));
+ var transToClean = await _store.GetTransactionsAsync(lastTranId, visibility, cancellationToken, DateTime.UtcNow.AddDays(-1 * RetentionPeriodDays));
_logger.LogInformation($"{Name}: found transactions={transToClean.Count}.");
@@ -63,8 +62,7 @@ protected override async Task RunWorkAsync(CancellationToken cancellationToken)
}
var totalRows = 0;
- foreach ((long TransactionId, DateTime? VisibleDate, DateTime? InvisibleHistoryRemovedDate) tran in
- transToClean.Where(x => !x.InvisibleHistoryRemovedDate.HasValue).OrderBy(x => x.TransactionId))
+ foreach (var tran in transToClean.Where(x => !x.InvisibleHistoryRemovedDate.HasValue).OrderBy(x => x.TransactionId))
{
var rows = await _store.MergeResourcesDeleteInvisibleHistory(tran.TransactionId, cancellationToken);
_logger.LogInformation($"{Name}: transaction={tran.TransactionId} removed rows={rows}.");
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Microsoft.Health.Fhir.SqlServer.csproj b/src/Microsoft.Health.Fhir.SqlServer/Microsoft.Health.Fhir.SqlServer.csproj
index 909f001e89..b5edf826d1 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Microsoft.Health.Fhir.SqlServer.csproj
+++ b/src/Microsoft.Health.Fhir.SqlServer/Microsoft.Health.Fhir.SqlServer.csproj
@@ -1,7 +1,7 @@

- 84
+ 85
Features\Schema\Migrations\$(LatestSchemaVersion).sql
diff --git a/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Import/ImportTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Import/ImportTests.cs
index 63546f7646..ecaa9bcfbb 100644
--- a/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Import/ImportTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Import/ImportTests.cs
@@ -53,6 +53,10 @@ public ImportTests(ImportTestFixture fixture)
_client = fixture.TestFhirClient;
_metricHandler = fixture.MetricHandler;
_fixture = fixture;
+ if (_fixture.IsUsingInProcTestServer)
+ {
+ ExecuteSql("INSERT INTO Parameters (Id,Char) SELECT 'MergeResources.AdlsConnectionString','UseDevelopmentStorage=true'");
+ }
}
[Fact]
@@ -138,7 +142,7 @@ CREATE TRIGGER Transactions_Trigger ON Transactions FOR UPDATE
var message = await ImportWaitAsync(registration.CheckLocation, false);
Assert.Equal(requestedExceptions == 6 ? HttpStatusCode.InternalServerError : HttpStatusCode.OK, message.StatusCode);
var retries = (int)ExecuteSql("SELECT count(*) FROM EventLog WHERE Process = 'MergeResourcesCommitTransaction' AND Status = 'Error'");
- Assert.Equal(requestedExceptions == 6 ? 5 : 3, retries);
+ Assert.Equal(requestedExceptions == 6 ? 4 : 3, retries);
}
finally
{
diff --git a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/ChangeFeed/SqlServerFhirResourceChangeCaptureEnabledTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/ChangeFeed/SqlServerFhirResourceChangeCaptureEnabledTests.cs
index f6834853e3..497839ea7b 100644
--- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/ChangeFeed/SqlServerFhirResourceChangeCaptureEnabledTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/ChangeFeed/SqlServerFhirResourceChangeCaptureEnabledTests.cs
@@ -102,7 +102,7 @@ public async Task GivenADatabaseSupportsResourceChangeCapture_WhenUpdatingAResou
[Fact]
public async Task GivenADatabaseSupportsResourceChangeCapture_WhenImportingNegativeVersions_ThenResourceChangesShouldBeReturned()
{
- ExecuteSql("TRUNCATE TABLE dbo.Resource");
+ ExecuteSql("DELETE FROM dbo.Resource");
var store = (SqlServerFhirDataStore)_fixture.DataStore;
@@ -133,7 +133,7 @@ public async Task GivenChangeCaptureEnabledAndNoVersionPolicy_AfterUpdating_Invi
{
EnableInvisibleHistory();
ExecuteSql("TRUNCATE TABLE dbo.Transactions");
- ExecuteSql("TRUNCATE TABLE dbo.Resource");
+ ExecuteSql("DELETE FROM dbo.Resource");
var store = (SqlServerFhirDataStore)_fixture.DataStore;
@@ -189,7 +189,7 @@ public async Task GivenChangeCaptureEnabledAndNoVersionPolicy_AfterHardDeleting_
{
EnableInvisibleHistory();
ExecuteSql("TRUNCATE TABLE dbo.Transactions");
- ExecuteSql("TRUNCATE TABLE dbo.Resource");
+ ExecuteSql("DELETE FROM dbo.Resource");
var store = (SqlServerFhirDataStore)_fixture.DataStore;
diff --git a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/SqlServerExportTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/SqlServerExportTests.cs
index cc720869b7..1c91f9eb3e 100644
--- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/SqlServerExportTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/SqlServerExportTests.cs
@@ -75,7 +75,7 @@ public async Task ExportWorkRegistration()
finally
{
ExecuteSql("TRUNCATE TABLE dbo.JobQueue");
- ExecuteSql("TRUNCATE TABLE dbo.Resource");
+ ExecuteSql("DELETE FROM dbo.Resource");
ExecuteSql(DropTrigger);
}
}
@@ -151,23 +151,29 @@ FOR INSERT
private void PrepareData()
{
ExecuteSql("TRUNCATE TABLE dbo.JobQueue");
- ExecuteSql("TRUNCATE TABLE dbo.Resource");
+ ExecuteSql("DELETE FROM dbo.ResourceIdIntMap");
+ ExecuteSql("DELETE FROM dbo.Resource");
var surrId = DateTimeOffset.UtcNow.ToId();
ExecuteSql(@$"
+INSERT INTO ResourceIdIntMap
+ (ResourceTypeId, ResourceId, ResourceIdInt)
+ SELECT ResourceTypeId, newid(), RowId
+ FROM (SELECT RowId FROM (SELECT RowId = row_number() OVER (ORDER BY A1.id) FROM syscolumns A1, syscolumns A2) A WHERE RowId <= 1000) A
+ CROSS JOIN (SELECT ResourceTypeId FROM dbo.ResourceType WHERE Name IN ('Patient','Observation','Claim')) B
+
INSERT INTO Resource
- (ResourceTypeId,ResourceId,Version,IsHistory,ResourceSurrogateId,IsDeleted,RequestMethod,RawResource,IsRawResourceMetaSet,SearchParamHash)
+ (ResourceTypeId,ResourceIdInt,Version,IsHistory,ResourceSurrogateId,IsDeleted,RequestMethod,RawResource,IsRawResourceMetaSet,SearchParamHash)
SELECT ResourceTypeId
- ,newid()
+ ,ResourceIdInt
,1
,0
- ,{surrId} - RowId * 1000 -- go to the past
+ ,{surrId} - ResourceIdInt * 1000 -- go to the past
,0
,null
,0x12345
,1
,null
- FROM (SELECT RowId FROM (SELECT RowId = row_number() OVER (ORDER BY A1.id) FROM syscolumns A1, syscolumns A2) A WHERE RowId <= 1000) A
- CROSS JOIN (SELECT ResourceTypeId FROM dbo.ResourceType WHERE Name IN ('Patient','Observation','Claim')) B
+ FROM ResourceIdIntMap B
");
}
diff --git a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/FhirStorageTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/FhirStorageTests.cs
index 8187afa22a..3eb7e297db 100644
--- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/FhirStorageTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/FhirStorageTests.cs
@@ -79,10 +79,10 @@ public async Task RetriesOnConflict(int requestedExceptions)
{
await _fixture.SqlHelper.ExecuteSqlCmd("TRUNCATE TABLE EventLog");
await _fixture.SqlHelper.ExecuteSqlCmd(@$"
-CREATE TRIGGER Resource_Trigger ON Resource FOR INSERT
+CREATE TRIGGER Resource_Trigger ON CurrentResources FOR INSERT
AS
IF (SELECT count(*) FROM EventLog WHERE Process = 'MergeResources' AND Status = 'Error') < {requestedExceptions}
- INSERT INTO Resource SELECT * FROM inserted -- this will cause dup key exception which is treated as a conflict
+ INSERT INTO CurrentResources SELECT * FROM inserted -- this will cause dup key exception which is treated as a conflict
");
var patient = (Patient)Samples.GetJsonSample("Patient").ToPoco();
@@ -118,6 +118,7 @@ INSERT INTO Resource SELECT * FROM inserted -- this will cause dup key exception
public async Task TimeTravel()
{
await _fixture.SqlHelper.ExecuteSqlCmd("DELETE FROM dbo.Resource"); // remove all data
+ await _fixture.SqlHelper.ExecuteSqlCmd("DELETE FROM dbo.ResourceIdIntMap"); // remove all data
// add resource
var type = "Patient";
@@ -196,15 +197,23 @@ public async Task TimeTravel()
private async Task UpdateResource(Patient patient)
{
var oldId = patient.Id;
- await _fixture.SqlHelper.ExecuteSqlCmd($"UPDATE dbo.Resource SET IsHistory = 1 WHERE ResourceId = '{oldId}' AND Version = 1");
+ await _fixture.SqlHelper.ExecuteSqlCmd(@$"
+UPDATE dbo.Resource SET IsHistory = 1 WHERE ResourceIdInt = (SELECT ResourceIdInt FROM ResourceIdIntMap WHERE ResourceId = '{oldId}') AND Version = 1
+ ");
var newId = Guid.NewGuid().ToString();
patient.Id = newId;
await Mediator.UpsertResourceAsync(patient.ToResourceElement()); // there is no control to keep history, so insert as new and update to old
- await _fixture.SqlHelper.ExecuteSqlCmd($"UPDATE dbo.Resource SET ResourceId = '{oldId}', Version = 2, IsHistory = 1 WHERE ResourceId = '{newId}' AND Version = 1");
+ await _fixture.SqlHelper.ExecuteSqlCmd(@$"
+UPDATE dbo.Resource SET IsHistory = 1 WHERE ResourceIdInt = (SELECT ResourceIdInt FROM ResourceIdIntMap WHERE ResourceId = '{newId}') AND Version = 1
+UPDATE dbo.HistoryResources SET ResourceIdInt = (SELECT ResourceIdInt FROM ResourceIdIntMap WHERE ResourceId = '{oldId}'), Version = 2 WHERE ResourceIdInt = (SELECT ResourceIdInt FROM ResourceIdIntMap WHERE ResourceId = '{newId}') AND Version = 1
+ ");
newId = Guid.NewGuid().ToString();
patient.Id = newId;
await Mediator.UpsertResourceAsync(patient.ToResourceElement()); // there is no control to keep history, so insert as new and update to old
- await _fixture.SqlHelper.ExecuteSqlCmd($"UPDATE dbo.Resource SET ResourceId = '{oldId}', Version = 3 WHERE ResourceId = '{newId}' AND Version = 1");
+ //// noramlly we do not allow update in place
+ await _fixture.SqlHelper.ExecuteSqlCmd(@$"
+UPDATE dbo.CurrentResources SET ResourceIdInt = (SELECT ResourceIdInt FROM ResourceIdIntMap WHERE ResourceId = '{oldId}'), Version = 3 WHERE ResourceIdInt = (SELECT ResourceIdInt FROM ResourceIdIntMap WHERE ResourceId = '{newId}') AND Version = 1
+ ");
}
[Fact]
diff --git a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerFhirStorageTestHelper.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerFhirStorageTestHelper.cs
index f9ff1e61af..55b60ffb28 100644
--- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerFhirStorageTestHelper.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerFhirStorageTestHelper.cs
@@ -67,7 +67,9 @@ public SqlServerFhirStorageTestHelper(
public async Task CreateAndInitializeDatabase(string databaseName, int maximumSupportedSchemaVersion, bool forceIncrementalSchemaUpgrade, SchemaInitializer schemaInitializer = null, CancellationToken cancellationToken = default)
{
- string testConnectionString = new SqlConnectionStringBuilder(_initialConnectionString) { InitialCatalog = databaseName }.ToString();
+ var builder = new SqlConnectionStringBuilder(_initialConnectionString) { InitialCatalog = databaseName };
+ var isLocal = builder.DataSource.Contains("local", StringComparison.OrdinalIgnoreCase);
+ var testConnectionString = builder.ToString();
await _dbSetupRetryPolicy.ExecuteAsync(
async () =>
@@ -102,21 +104,39 @@ IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = '{databaseName}')
await _dbSetupRetryPolicy.ExecuteAsync(
async () =>
{
- await using SqlConnection connection = await _sqlConnectionBuilder.GetSqlConnectionAsync(databaseName, null, cancellationToken);
- await connection.OpenAsync(cancellationToken);
- await using SqlCommand sqlCommand = connection.CreateCommand();
- sqlCommand.CommandText = "IF object_id('sp_changedbowner') IS NOT NULL EXECUTE sp_changedbowner 'sa'";
- await sqlCommand.ExecuteNonQueryAsync(cancellationToken);
- await connection.CloseAsync();
+ using var conn = await _sqlConnectionBuilder.GetSqlConnectionAsync(databaseName, null, cancellationToken);
+ using var cmd = new SqlCommand("IF object_id('sp_changedbowner') IS NOT NULL EXECUTE sp_changedbowner 'sa'", conn);
+ await conn.OpenAsync(cancellationToken);
+ cmd.ExecuteNonQuery();
});
schemaInitializer ??= CreateSchemaInitializer(testConnectionString, maximumSupportedSchemaVersion);
await _dbSetupRetryPolicy.ExecuteAsync(async () => { await schemaInitializer.InitializeAsync(forceIncrementalSchemaUpgrade, cancellationToken); });
await InitWatchdogsParameters(databaseName);
await EnableDatabaseLogging(databaseName);
+ if (isLocal)
+ {
+ await EnableRawResourcesInAdls(databaseName);
+ }
+
await _sqlServerFhirModel.Initialize(maximumSupportedSchemaVersion, cancellationToken);
}
+ public async Task EnableRawResourcesInAdls(string databaseName)
+ {
+ await _dbSetupRetryPolicy.ExecuteAsync(async () =>
+ {
+ await using SqlConnection connection = await _sqlConnectionBuilder.GetSqlConnectionAsync(databaseName, cancellationToken: CancellationToken.None);
+ await connection.OpenAsync(CancellationToken.None);
+ await using SqlCommand sqlCommand = connection.CreateCommand();
+ sqlCommand.CommandText = @"
+INSERT INTO Parameters (Id,Char) SELECT 'MergeResources.AdlsConnectionString','UseDevelopmentStorage=true'
+ ";
+ await sqlCommand.ExecuteNonQueryAsync(CancellationToken.None);
+ await connection.CloseAsync();
+ });
+ }
+
public async Task EnableDatabaseLogging(string databaseName)
{
await _dbSetupRetryPolicy.ExecuteAsync(async () =>
@@ -301,10 +321,10 @@ async Task IFhirStorageTestHelper.ValidateSnapshotTokenIsCurrent(object snapshot
await using (SqlCommand outerCommand = connection.CreateCommand())
{
outerCommand.CommandText = @"
- SELECT t.name
- FROM sys.tables t
- INNER JOIN sys.columns c ON c.object_id = t.object_id
- WHERE c.name = 'ResourceSurrogateId'";
+SELECT t.name
+ FROM (SELECT name, object_id FROM sys.objects WHERE name NOT IN ('CurrentResource', 'CurrentResources', 'HistoryResources', 'RawResources') AND type IN ('u','v')) t
+ JOIN sys.columns c ON c.object_id = t.object_id
+ WHERE c.name = 'ResourceSurrogateId'";
await using (SqlDataReader reader = await outerCommand.ExecuteReaderAsync())
{
@@ -316,7 +336,7 @@ FROM sys.tables t
}
string tableName = reader.GetString(0);
- sb.AppendLine($"SELECT '{tableName}' as TableName, MAX(ResourceSurrogateId) as MaxResourceSurrogateId FROM dbo.{tableName}");
+ sb.AppendLine($"SELECT '{tableName}', max(ResourceSurrogateId) FROM dbo.{tableName} {(tableName == "Resource" ? "WHERE RawResource <> 0xF" : string.Empty)}");
}
}
}
diff --git a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerSchemaUpgradeTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerSchemaUpgradeTests.cs
index 44292acfe3..b0c3949ddf 100644
--- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerSchemaUpgradeTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerSchemaUpgradeTests.cs
@@ -285,7 +285,7 @@ private async Task CompareDatabaseSchemas(string databaseName1, string d
{
//// Our home grown SQL schema generator does not understand that statements can be formatted differently but contain identical SQL
//// Skipping some objects
- var objectsToSkip = new[] { "GetResourceSearchParamStats", "MergeResourcesAdvanceTransactionVisibility", "DequeueJob", "DisableIndexes", "GetResourceVersions", "CleanupEventLog", "InitDefrag", "EnqueueJobs", "GetResourcesByTypeAndSurrogateIdRange", "GetResourceSurrogateIdRanges", "GetCommandsForRebuildIndexes", "GetIndexCommands", "SwitchPartitionsIn", "SwitchPartitionsOut" }.ToList();
+ var objectsToSkip = new[] { "GetResourceSearchParamStats", "DequeueJob", "DisableIndexes", "GetResourceVersions", "EnqueueJobs", "GetResourceSurrogateIdRanges", "GetCommandsForRebuildIndexes", "GetIndexCommands", "SwitchPartitionsIn", "SwitchPartitionsOut" }.ToList();
objectsToSkip.Add("PartitionFunction_ResourceChangeData_Timestamp"); // definition is not predictable as it has start time component
if (schemaDifference.SourceObject != null && objectsToSkip.Any(_ => schemaDifference.SourceObject.Name.ToString().Contains(_)))
{
@@ -294,8 +294,8 @@ private async Task CompareDatabaseSchemas(string databaseName1, string d
if (schemaDifference.SourceObject != null
&& schemaDifference.TargetObject != null
- && schemaDifference.SourceObject.ObjectType.Name == "Procedure"
- && await IsStoredProcedureTextEqual(testConnectionString1, testConnectionString2, schemaDifference.SourceObject.Name.ToString()))
+ && (schemaDifference.SourceObject.ObjectType.Name == "Procedure" || schemaDifference.SourceObject.ObjectType.Name == "View")
+ && await IsObjectTextEqual(testConnectionString1, testConnectionString2, schemaDifference.SourceObject.Name.ToString()))
{
continue;
}
@@ -316,10 +316,10 @@ private async Task CompareDatabaseSchemas(string databaseName1, string d
return unexpectedDifference.ToString();
}
- private async Task IsStoredProcedureTextEqual(string connStr1, string connStr2, string storedProcedureName)
+ private async Task IsObjectTextEqual(string connStr1, string connStr2, string objectName)
{
- var text1 = await GetStoredProcedureText(connStr1, storedProcedureName);
- var text2 = await GetStoredProcedureText(connStr2, storedProcedureName);
+ var text1 = await GetObjectText(connStr1, objectName);
+ var text2 = await GetObjectText(connStr2, objectName);
text1 = Normalize(text1);
text2 = Normalize(text2);
@@ -368,7 +368,7 @@ private string Normalize(string text)
.Replace("))on", ")on");
}
- private async Task GetStoredProcedureText(string connStr, string storedProcedureName)
+ private async Task GetObjectText(string connStr, string storedProcedureName)
{
using var conn = new SqlConnection(connStr);
await conn.OpenAsync();
diff --git a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerTransactionScopeTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerTransactionScopeTests.cs
index 38e8fe9fac..ea468222fb 100644
--- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerTransactionScopeTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerTransactionScopeTests.cs
@@ -43,9 +43,13 @@ public async Task GivenATransactionScope_WhenReading_TheUncommittedValuesShouldO
using (SqlCommandWrapper sqlCommandWrapper = connectionWrapperWithTransaction.CreateRetrySqlCommand())
{
sqlCommandWrapper.CommandText = @"
- INSERT INTO Resource
- (ResourceTypeId,ResourceId,Version,IsHistory,ResourceSurrogateId,IsDeleted,RequestMethod,RawResource,IsRawResourceMetaSet,SearchParamHash)
- VALUES(97, @newId, 1, 0, 5095719085917680000, 0, null, CAST('test' AS VARBINARY(MAX)), 0, @searchParamHash)";
+INSERT INTO dbo.ResourceIdIntMap
+ (ResourceTypeId, ResourceIdInt,ResourceId)
+ SELECT 97,5095719085917680000, @newId
+INSERT INTO dbo.Resource
+ (ResourceTypeId, ResourceIdInt,Version,IsHistory,ResourceSurrogateId,IsDeleted,IsRawResourceMetaSet, RawResource, SearchParamHash)
+ SELECT 97,5095719085917680000, 1, 0,5095719085917680000, 0, 0,CAST('test' AS varbinary(max)), @searchParamHash
+ ";
sqlCommandWrapper.Parameters.Add(new SqlParameter { ParameterName = "newId", Value = newId });
sqlCommandWrapper.Parameters.Add(new SqlParameter { ParameterName = "searchParamHash", Value = searchParamHash });
@@ -97,9 +101,12 @@ public async Task GivenATransactionScope_WhenReadingAfterComplete_TheValuesShoul
using (SqlCommandWrapper sqlCommandWrapper = connectionWrapperWithTransaction.CreateRetrySqlCommand())
{
sqlCommandWrapper.CommandText = @"
- INSERT INTO Resource
- (ResourceTypeId,ResourceId,Version,IsHistory,ResourceSurrogateId,IsDeleted,RequestMethod,RawResource,IsRawResourceMetaSet,SearchParamHash)
- VALUES(97, @newId, 1, 0, 5095719085917680001, 0, null, CAST('test' AS VARBINARY(MAX)), 0, @searchParamHash)";
+INSERT INTO dbo.ResourceIdIntMap
+ (ResourceTypeId, ResourceIdInt,ResourceId)
+ SELECT 97,5095719085917680001, @newId
+INSERT INTO Resource
+ (ResourceTypeId, ResourceIdInt,Version,IsHistory,ResourceSurrogateId,IsDeleted, RawResource,IsRawResourceMetaSet, SearchParamHash)
+ SELECT 97,5095719085917680001, 1, 0,5095719085917680001, 0,CAST('test' AS varbinary(max)), 0,@searchParamHash";
sqlCommandWrapper.Parameters.Add(new SqlParameter { ParameterName = "newId", Value = newId });
sqlCommandWrapper.Parameters.Add(new SqlParameter { ParameterName = "searchParamHash", Value = searchParamHash });
diff --git a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerWatchdogTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerWatchdogTests.cs
index 3d3134428f..e87092b14e 100644
--- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerWatchdogTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlServerWatchdogTests.cs
@@ -164,7 +164,7 @@ WHILE @i < 10000
public async Task RollTransactionForward()
{
ExecuteSql("TRUNCATE TABLE dbo.Transactions");
- ExecuteSql("TRUNCATE TABLE dbo.Resource");
+ ExecuteSql("DELETE FROM dbo.Resource");
ExecuteSql("TRUNCATE TABLE dbo.NumberSearchParam");
using var cts = new CancellationTokenSource();
@@ -353,7 +353,7 @@ private long GetCount(string table)
{
using var conn = new SqlConnection(_fixture.TestConnectionString);
conn.Open();
- using var cmd = new SqlCommand($"SELECT sum(row_count) FROM sys.dm_db_partition_stats WHERE object_id = object_id('{table}') AND index_id IN (0,1)", conn);
+ using var cmd = new SqlCommand($"SELECT count_big(*) FROM {table}", conn);
var res = cmd.ExecuteScalar();
return (long)res;
}
diff --git a/tools/BlobRewriter/BlobRewriter.csproj b/tools/BlobRewriter/BlobRewriter.csproj
index d166335c00..f8bf98667e 100644
--- a/tools/BlobRewriter/BlobRewriter.csproj
+++ b/tools/BlobRewriter/BlobRewriter.csproj
@@ -1,4 +1,4 @@
-
+
Exe
diff --git a/tools/Exporter/App.config b/tools/Exporter/App.config
index f653b9471c..5f6e6af990 100644
--- a/tools/Exporter/App.config
+++ b/tools/Exporter/App.config
@@ -1,11 +1,12 @@

-
+
-
-
+
+
+
diff --git a/tools/Exporter/Exporter.csproj b/tools/Exporter/Exporter.csproj
index 99a406015a..daf9b05fc1 100644
--- a/tools/Exporter/Exporter.csproj
+++ b/tools/Exporter/Exporter.csproj
@@ -13,9 +13,8 @@
-
+
-
diff --git a/tools/Exporter/Program.cs b/tools/Exporter/Program.cs
index 58f8f02c66..c0e2aeac17 100644
--- a/tools/Exporter/Program.cs
+++ b/tools/Exporter/Program.cs
@@ -6,27 +6,31 @@
using System;
using System.Collections.Generic;
using System.Configuration;
-using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Azure.Identity;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Specialized;
-using Microsoft.Health.Fhir.SqlServer.Features;
-using Microsoft.Health.Fhir.Store.Export;
+////using Azure.Storage.Files.DataLake;
+using Microsoft.AspNetCore.Components.Forms;
+using Microsoft.Data.SqlClient;
using Microsoft.Health.Fhir.Store.Utils;
-using Microsoft.Health.Internal.Fhir.Sql;
+using Microsoft.Health.Internal.Fhir.Exporter;
+using Task = System.Threading.Tasks.Task;
-namespace Microsoft.Health.Internal.Fhir.Exporter
+namespace Microsoft.Health.Fhir.Store.Export
{
public static class Program
{
- private static readonly string _connectionString = ConfigurationManager.ConnectionStrings["Database"].ConnectionString;
- private static readonly SqlService Store = new SqlService(_connectionString);
- private static readonly string BlobConnectionString = ConfigurationManager.AppSettings["BlobConnectionString"];
- private static readonly string BlobContainerName = ConfigurationManager.AppSettings["BlobContainerName"];
+ private static readonly string ConnectionString = ConfigurationManager.ConnectionStrings["Database"].ConnectionString;
+ private static readonly string AdlsUri = ConfigurationManager.AppSettings["AdlsUri"];
+ private static readonly string AdlsUAMI = ConfigurationManager.AppSettings["AdlsUAMI"];
+ private static readonly string AdlsContainerName = ConfigurationManager.AppSettings["AdlsContainerName"];
private static readonly int Threads = int.Parse(ConfigurationManager.AppSettings["Threads"]);
private static readonly string ResourceType = ConfigurationManager.AppSettings["ResourceType"];
private static readonly int UnitSize = int.Parse(ConfigurationManager.AppSettings["UnitSize"]);
@@ -38,6 +42,7 @@ public static class Program
private static readonly int ReportingPeriodSec = int.Parse(ConfigurationManager.AppSettings["ReportingPeriodSec"]);
private static readonly DateTime StartDate = DateTime.Parse(ConfigurationManager.AppSettings["StartDate"]);
private static readonly DateTime EndDate = DateTime.Parse(ConfigurationManager.AppSettings["EndDate"]);
+ private static readonly SqlService Source = new SqlService(ConnectionString);
private static bool stop = false;
private static long _resourcesTotal = 0L;
private static Stopwatch _swReport = Stopwatch.StartNew();
@@ -46,33 +51,317 @@ public static class Program
private static Stopwatch _database = new Stopwatch();
private static Stopwatch _unzip = new Stopwatch();
private static Stopwatch _blob = new Stopwatch();
+ private static BlobContainerClient _blobContainer;
+ ////private static DataLakeFileSystemClient _fileSystem;
public static void Main(string[] args)
{
- Console.WriteLine($"Source=[{_connectionString}]");
- if (args.Length > 0 && args[0] == "noqueue")
+ if (args.Length > 0 && (args[0] == "random" || args[0] == "sorted"))
{
- ExportNoQueue();
+ var count = args.Length > 1 ? int.Parse(args[1]) : 100;
+ _blobContainer = GetContainer(AdlsUri, AdlsUAMI, AdlsContainerName);
+ ////_fileSystem = new DataLakeFileSystemClient(new Uri($"{AdlsUri}/{AdlsContainerName}"), string.IsNullOrEmpty(AdlsUAMI) ? new InteractiveBrowserCredential() : new ManagedIdentityCredential(AdlsUAMI));
+ ////try
+ ////{
+ //// _fileSystem.GetFileClient("blobName").OpenRead();
+ ////}
+ ////catch // Ignore
+ ////{
+ //// return;
+ ////}
+
+ var parall = args.Length > 2 ? int.Parse(args[2]) : 8;
+
+ if (args[0] == "random")
+ {
+ RandomReads(count, parall);
+ }
+ else
+ {
+ SortedReads(count, parall);
+ }
+ }
+ else if (args.Length == 0 || args[0] == "storage")
+ {
+ var count = args.Length > 1 ? int.Parse(args[1]) : 100;
+ var bufferKB = args.Length > 2 ? int.Parse(args[2]) : 20;
+ var parall = args.Length > 3 ? int.Parse(args[3]) : 1;
+ WriteAndReadAdls(count, bufferKB);
+ WriteAndReadBlob(count, bufferKB, parall);
}
else
{
- if (RebuildWorkQueue)
+ Console.WriteLine($"Source=[{Source.ShowConnectionString()}]");
+ if (args.Length > 0 && args[0] == "noqueue")
{
- PopulateJobQueue(ResourceType, UnitSize);
+ ExportNoQueue();
+ }
+ else
+ {
+ if (RebuildWorkQueue)
+ {
+ PopulateJobQueue(ResourceType, UnitSize);
+ }
+
+ Export();
}
+ }
+ }
+
+ public static void RandomReads(int count, int parall)
+ {
+ var maxId = LastUpdatedToResourceSurrogateId(DateTime.UtcNow);
+ var ranges = Source.GetSurrogateIdRanges(96, 0, maxId, 10000, 10000);
+ var refs = new List<(long FileId, int OffsetInFile)>();
+ foreach (var range in ranges)
+ {
+ refs.AddRange(Source.GetRefs(96, range.StartId, range.EndId));
+ }
+
+ Console.WriteLine($"RandomRead: file/offsets = {refs.Count}");
+
+ var blobDurations = new List();
+ var fileDurations = new List();
+ for (var l = 0; l < 10; l++)
+ {
+ var subSetRefs = refs.OrderBy(_ => RandomNumberGenerator.GetInt32(100000000)).Take(count).ToList();
+ var sw = Stopwatch.StartNew();
+ var resources = GetRawResourceFromAdls(subSetRefs, true, parall);
+ blobDurations.Add(sw.Elapsed.TotalMilliseconds);
+ Console.WriteLine($"BLOB.RandomRead.{resources.Count}.parall={parall}: total={sw.Elapsed.TotalMilliseconds} msec perLine={sw.Elapsed.TotalMilliseconds / resources.Count} msec");
+ subSetRefs = refs.OrderBy(_ => RandomNumberGenerator.GetInt32(100000000)).Take(count).ToList();
+ sw = Stopwatch.StartNew();
+ resources = GetRawResourceFromAdls(subSetRefs, false, parall);
+ fileDurations.Add(sw.Elapsed.TotalMilliseconds);
+ Console.WriteLine($"File.RandomRead.{resources.Count}.parall={parall}: total={sw.Elapsed.TotalMilliseconds} msec perLine={sw.Elapsed.TotalMilliseconds / resources.Count} msec");
+ }
+
+ Console.WriteLine($"BLOB.RandomRead.parall={parall}: total={blobDurations.Sum() / 10} msec");
+ Console.WriteLine($"File.RandomRead.parall={parall}: total={fileDurations.Sum() / 10} msec");
+ }
+
+ public static void SortedReads(int count, int parall)
+ {
+ var maxId = LastUpdatedToResourceSurrogateId(DateTime.UtcNow);
+ var ranges = Source.GetSurrogateIdRanges(96, 0, maxId, 10000, 10000);
+ var refs = new List<(long FileId, int OffsetInFile)>();
+ foreach (var range in ranges)
+ {
+ refs.AddRange(Source.GetRefs(96, range.StartId, range.EndId));
+ }
+
+ Console.WriteLine($"SortedRead: file/offsets = {refs.Count}");
+
+ var blobDurations = new List();
+ var fileDurations = new List();
+ var blobResources = 0L;
+ var fileResources = 0L;
+ var loop = 0;
+ foreach (var r in refs.GroupBy(_ => _.FileId))
+ {
+ var subSetRefs = r.ToList();
+
+ var sw = Stopwatch.StartNew();
+ var resources = GetRawResourceFromAdls(subSetRefs, true, parall);
+ Console.WriteLine($"Ignore BLOB.SortedRead.{resources.Count}.parall={parall}: total={sw.Elapsed.TotalMilliseconds} msec perLine={sw.Elapsed.TotalMilliseconds / resources.Count} msec");
+
+ sw = Stopwatch.StartNew();
+ resources = GetRawResourceFromAdls(subSetRefs, false, parall);
+ fileDurations.Add(sw.Elapsed.TotalMilliseconds);
+ fileResources += resources.Sum(_ => _.Length);
+ Console.WriteLine($"File.SortedRead.{resources.Count}.parall={parall}: total={sw.Elapsed.TotalMilliseconds} msec perLine={sw.Elapsed.TotalMilliseconds / resources.Count} msec");
+
+ sw = Stopwatch.StartNew();
+ resources = GetRawResourceFromAdls(subSetRefs, true, parall);
+ blobDurations.Add(sw.Elapsed.TotalMilliseconds);
+ blobResources += resources.Sum(_ => _.Length);
+ Console.WriteLine($"BLOB.SortedRead.{resources.Count}.parall={parall}: total={sw.Elapsed.TotalMilliseconds} msec perLine={sw.Elapsed.TotalMilliseconds / resources.Count} msec");
+
+ loop++;
+ if (loop >= 10)
+ {
+ break;
+ }
+ }
+
+ Console.WriteLine($"BLOB.SortedRead.parall={parall}: resources={blobResources} total={blobDurations.Sum()} msec");
+ Console.WriteLine($"File.SortedRead.parall={parall}: resources={fileResources} total={fileDurations.Sum()} msec");
+ }
+
+ public static IReadOnlyList GetRawResourceFromAdls(IReadOnlyList<(long FileId, int OffsetInFile)> resourceRefs, bool isBlob, int parall)
+ {
+ var start = DateTime.UtcNow;
+ var results = new List();
+ if (resourceRefs == null || resourceRefs.Count == 0)
+ {
+ return results;
+ }
+
+ if (isBlob)
+ {
+ var resourceRefsByTransaction = resourceRefs.GroupBy(_ => _.FileId);
+ Parallel.ForEach(resourceRefsByTransaction, new ParallelOptions { MaxDegreeOfParallelism = parall }, (group) =>
+ {
+ var transactionId = group.Key;
+ var blobName = GetBlobName(transactionId);
+ var blobClient = _blobContainer.GetBlobClient(blobName);
+ using var stream = blobClient.OpenRead();
+ using var reader = new StreamReader(stream);
+ foreach (var resourceRef in group)
+ {
+ reader.DiscardBufferedData();
+ stream.Position = resourceRef.OffsetInFile;
+ var line = reader.ReadLine();
+ lock (results)
+ {
+ results.Add(line);
+ }
+ }
+ });
+ }
+ else
+ {
+ ////var resourceRefsByTransaction = resourceRefs.GroupBy(_ => _.FileId);
+ ////Parallel.ForEach(resourceRefsByTransaction, new ParallelOptions { MaxDegreeOfParallelism = parall }, (group) =>
+ ////{
+ //// var transactionId = group.Key;
+ //// var blobName = GetBlobName(transactionId);
+ //// var fileClient = _fileSystem.GetFileClient(blobName);
+ //// using var stream = fileClient.OpenRead();
+ //// using var reader = new StreamReader(stream);
+ //// foreach (var resourceRef in group)
+ //// {
+ //// reader.DiscardBufferedData();
+ //// stream.Position = resourceRef.OffsetInFile;
+ //// var line = reader.ReadLine();
+ //// lock (results)
+ //// {
+ //// results.Add(line);
+ //// }
+ //// }
+ ////});
+ }
+
+ Source.LogEvent("GetRawResourceFromAdls", "Warn", $"Resources={results.Count}", start);
+
+ return results;
+ }
+
+ internal static string GetBlobName(long fileId)
+ {
+ return $"hash-{GetPermanentHashCode(fileId)}/transaction-{fileId}.ndjson";
+ }
- Export();
+ private static string GetPermanentHashCode(long tr)
+ {
+ var hashCode = 0;
+ foreach (var c in tr.ToString()) // Don't convert to LINQ. This is 10% faster.
+ {
+ hashCode = unchecked((hashCode * 251) + c);
}
+
+ return (Math.Abs(hashCode) % 512).ToString().PadLeft(3, '0');
+ }
+
+ public static void WriteAndReadAdls(int count, int bufferKB)
+ {
+ GetContainer(AdlsUri, AdlsUAMI, "fhir-hs-new-one-file");
+
+ ////var fileName = "transaction-353229202.ndjson";
+
+ ////var swGlobal = Stopwatch.StartNew();
+
+ ////var fileSystem = new DataLakeFileSystemClient(new Uri(AdlsUri), string.IsNullOrEmpty(AdlsUAMI) ? new InteractiveBrowserCredential() : new ManagedIdentityCredential(AdlsUAMI));
+ ////var fileClient = fileSystem.GetFileClient($"fhir-hs-new-one-file/{fileName}");
+
+ ////var offests = new List();
+ ////var offset = 0;
+ ////var eol = Encoding.UTF8.GetByteCount(Environment.NewLine);
+
+ ////var baseLine = string.Concat(Enumerable.Repeat("0123456789", 200)); // 2KB
+
+ ////using var writeStream = fileClient.OpenWrite(true);
+ ////using var writer = new StreamWriter(writeStream);
+ ////for (var i = 0; i < count; i++)
+ ////{
+ //// offests.Add(offset);
+ //// var line = $"{offset}\t{baseLine}";
+ //// offset += Encoding.UTF8.GetByteCount(line) + eol;
+ //// writer.WriteLine(line);
+ ////}
+
+ ////writer.Flush();
+ ////Console.WriteLine($"ADLS.Write.{count}: total={swGlobal.Elapsed.TotalMilliseconds} msec perLine={swGlobal.Elapsed.TotalMilliseconds / count} msec");
+
+ ////swGlobal = Stopwatch.StartNew();
+ ////fileClient = fileSystem.GetFileClient($"testadls/{fileName}");
+ ////using var stream = fileClient.OpenRead(bufferSize: 1024 * bufferKB);
+ ////using var reader = new StreamReader(stream);
+ ////foreach (var pos in offests)
+ ////{
+ //// var sw = Stopwatch.StartNew();
+ //// reader.DiscardBufferedData();
+ //// stream.Position = pos;
+ //// var line = reader.ReadLine();
+ //// var readOffset = line.Split('\t')[0];
+ //// Console.WriteLine($"ADLS.Read.{count}.buffer={bufferKB}: {sw.Elapsed.TotalMilliseconds} msec (input,read)=({pos},{readOffset})");
+ ////}
+
+ ////Console.WriteLine($"ADLS.Read.{count}.buffer={bufferKB}: total={swGlobal.Elapsed.TotalMilliseconds} msec perLine={swGlobal.Elapsed.TotalMilliseconds / count} msec");
+ }
+
+ public static void WriteAndReadBlob(int count, int bufferKB, int parall)
+ {
+ var fileName = "test/test/test.txt";
+
+ var swGlobal = Stopwatch.StartNew();
+
+ var container = GetContainer(AdlsUri, AdlsUAMI, "testblob");
+
+ var offests = new List();
+ var offset = 0;
+ var eol = Encoding.UTF8.GetByteCount(Environment.NewLine);
+
+ var baseLine = string.Concat(Enumerable.Repeat("0123456789", 200)); // 2KB
+
+ using var writeStream = container.GetBlockBlobClient(fileName).OpenWrite(true);
+ using var writer = new StreamWriter(writeStream);
+ for (var i = 0; i < count; i++)
+ {
+ offests.Add(offset);
+ var line = $"{offset}\t{baseLine}";
+ offset += Encoding.UTF8.GetByteCount(line) + eol;
+ writer.WriteLine(line);
+ }
+
+ writer.Flush();
+
+ Console.WriteLine($"BLOB.Write.{count}: total={swGlobal.Elapsed.TotalMilliseconds} msec perLine={swGlobal.Elapsed.TotalMilliseconds / count} msec");
+
+ swGlobal = Stopwatch.StartNew();
+ container = GetContainer(AdlsUri, AdlsUAMI, "testblob");
+ var blobClient = container.GetBlobClient(fileName);
+ Parallel.ForEach(offests, new ParallelOptions { MaxDegreeOfParallelism = parall }, (pos) =>
+ {
+ var sw = Stopwatch.StartNew();
+ using var readStream = blobClient.OpenRead(pos, bufferSize: 1024 * bufferKB);
+ using var reader = new StreamReader(readStream);
+ var line = reader.ReadLine();
+ var readOffset = line.Split('\t')[0];
+ Console.WriteLine($"BLOB.Read.{count}: {sw.Elapsed.TotalMilliseconds} msec (input,read)=({pos},{readOffset})");
+ });
+
+ Console.WriteLine($"BLOB.Read.{count}.buffer={bufferKB}.parall={parall}: total={swGlobal.Elapsed.TotalMilliseconds} msec perLine={swGlobal.Elapsed.TotalMilliseconds / count} msec");
}
public static void ExportNoQueue()
{
var startId = LastUpdatedToResourceSurrogateId(StartDate);
var endId = LastUpdatedToResourceSurrogateId(EndDate);
- var resourceTypeId = Store.GetResourceTypeId(ResourceType);
- var ranges = Store.GetSurrogateIdRanges(resourceTypeId, startId, endId, UnitSize).ToList();
- Console.WriteLine($"ExportNoQueue.{ResourceType}: ranges={ranges.Count}.");
- var container = GetContainer(BlobConnectionString, BlobContainerName);
+ var resourceTypeId = Source.GetResourceTypeId(ResourceType);
+ var ranges = Source.GetSurrogateIdRanges(resourceTypeId, startId, endId, UnitSize, (int)(2e9 / UnitSize)).ToList();
+ Console.WriteLine($"ExportNoSource.{ResourceType}: ranges={ranges.Count}.");
+ var container = GetContainer(AdlsUri, AdlsUAMI, AdlsContainerName);
foreach (var range in ranges)
{
Export(resourceTypeId, container, range.StartId, range.EndId);
@@ -82,14 +371,14 @@ public static void ExportNoQueue()
{
if (_swReport.Elapsed.TotalSeconds > ReportingPeriodSec)
{
- Console.WriteLine($"ExportNoQueue.{ResourceType}.threads={Threads}.Writes={WritesEnabled}.Decompress={DecompressEnabled}.Reads={ReadsEnabled}: Resources={_resourcesTotal} secs={(int)_sw.Elapsed.TotalSeconds} speed={(int)(_resourcesTotal / _sw.Elapsed.TotalSeconds)} resources/sec DB={_database.Elapsed.TotalSeconds} sec UnZip={_unzip.Elapsed.TotalSeconds} sec Blob={_blob.Elapsed.TotalSeconds}");
+ Console.WriteLine($"ExportNoSource.{ResourceType}.threads={Threads}.Writes={WritesEnabled}.Decompress={DecompressEnabled}.Reads={ReadsEnabled}: Resources={_resourcesTotal} secs={(int)_sw.Elapsed.TotalSeconds} speed={(int)(_resourcesTotal / _sw.Elapsed.TotalSeconds)} resources/sec DB={_database.Elapsed.TotalSeconds} sec UnZip={_unzip.Elapsed.TotalSeconds} sec Blob={_blob.Elapsed.TotalSeconds}");
_swReport.Restart();
}
}
}
}
- Console.WriteLine($"ExportNoQueue.{ResourceType}.threads={Threads}: {(stop ? "FAILED" : "completed")} at {DateTime.Now:s}, resources={_resourcesTotal} speed={_resourcesTotal / _sw.Elapsed.TotalSeconds:N0} resources/sec elapsed={_sw.Elapsed.TotalSeconds:N0} sec DB={_database.Elapsed.TotalSeconds} sec UnZip={_unzip.Elapsed.TotalSeconds} sec Blob={_blob.Elapsed.TotalSeconds}");
+ Console.WriteLine($"ExportNoSource.{ResourceType}.threads={Threads}: {(stop ? "FAILED" : "completed")} at {DateTime.Now:s}, resources={_resourcesTotal} speed={_resourcesTotal / _sw.Elapsed.TotalSeconds:N0} resources/sec elapsed={_sw.Elapsed.TotalSeconds:N0} sec DB={_database.Elapsed.TotalSeconds} sec UnZip={_unzip.Elapsed.TotalSeconds} sec Blob={_blob.Elapsed.TotalSeconds}");
}
public static void Export()
@@ -120,15 +409,15 @@ private static void Export(int thread)
var version = 0L;
var retries = 0;
var maxRetries = MaxRetries;
- retry:
+retry:
try
{
- Store.DequeueJob(out var _, out unitId, out version, out resourceTypeId, out minId, out maxId);
+ Source.DequeueJob(out var _, out unitId, out version, out resourceTypeId, out minId, out maxId);
if (resourceTypeId.HasValue)
{
- var container = GetContainer(BlobConnectionString, BlobContainerName);
+ var container = GetContainer(AdlsUri, AdlsUAMI, AdlsContainerName);
var resources = Export(resourceTypeId.Value, container, long.Parse(minId), long.Parse(maxId));
- Store.CompleteJob(unitId, false, version, resources);
+ Source.CompleteJob(unitId, false, version, resources);
}
if (_swReport.Elapsed.TotalSeconds > ReportingPeriodSec)
@@ -146,9 +435,9 @@ private static void Export(int thread)
catch (Exception e)
{
Console.WriteLine($"Export.{ResourceType}.{thread}.{minId}.{maxId}: error={e}");
- Store.SqlRetryService.TryLogEvent($"Export:{ResourceType}.{thread}.{minId}.{maxId}", "Error", e.ToString(), null, CancellationToken.None).Wait();
+ Source.LogEvent($"Export", "Error", $"{ResourceType}.{thread}.{minId}.{maxId}: error={e}", DateTime.Now);
retries++;
- var isRetryable = e.IsRetriable();
+ var isRetryable = e.IsRetryable();
if (isRetryable)
{
maxRetries++;
@@ -163,7 +452,7 @@ private static void Export(int thread)
stop = true;
if (resourceTypeId.HasValue)
{
- Store.CompleteJob(unitId, true, version);
+ Source.CompleteJob(unitId, true, version);
}
throw;
@@ -181,7 +470,7 @@ private static int Export(short resourceTypeId, BlobContainerClient container, l
}
_database.Start();
- var resources = Store.GetDataBytes(resourceTypeId, minId, maxId).ToList(); // ToList will fource reading from SQL even when writes are disabled
+ var resources = Source.GetDataBytes(resourceTypeId, minId, maxId).ToList(); // ToList will fource reading from SQL even when writes are disabled
_database.Stop();
var strings = new List();
@@ -209,9 +498,9 @@ private static int Export(short resourceTypeId, BlobContainerClient container, l
return strings.Count;
}
- private static void WriteBatchOfLines(BlobContainerClient container, IReadOnlyCollection batch, string blobName)
+ private static void WriteBatchOfLines(BlobContainerClient container, IEnumerable batch, string blobName)
{
- retry:
+retry:
try
{
using var stream = container.GetBlockBlobClient(blobName).OpenWrite(true);
@@ -235,31 +524,29 @@ private static void WriteBatchOfLines(BlobContainerClient container, IReadOnlyCo
}
}
- private static BlobContainerClient GetContainer(string connectionString, string containerName)
+ private static BlobContainerClient GetContainer(string adlsUri, string adlsUAMI, string adlsContainerName)
{
try
{
- var blobServiceClient = new BlobServiceClient(connectionString);
- var blobContainerClient = blobServiceClient.GetBlobContainerClient(containerName);
+ if (string.IsNullOrEmpty(adlsUri))
+ {
+ throw new ArgumentNullException(nameof(adlsUri));
+ }
+
+ var blobServiceClient = new BlobServiceClient(new Uri(adlsUri), string.IsNullOrEmpty(adlsUAMI) ? new InteractiveBrowserCredential() : new ManagedIdentityCredential(adlsUAMI));
+ var blobContainerClient = blobServiceClient.GetBlobContainerClient(adlsContainerName);
if (!blobContainerClient.Exists())
{
- lock (_sw) // lock on anything global
- {
- blobContainerClient = blobServiceClient.GetBlobContainerClient(containerName);
- if (!blobContainerClient.Exists())
- {
- var container = blobServiceClient.CreateBlobContainer(containerName);
- Console.WriteLine($"Created container {container.Value.Name}");
- }
- }
+ var container = blobServiceClient.CreateBlobContainer(adlsContainerName);
+ Console.WriteLine($"Created container {container.Value.Name}");
}
return blobContainerClient;
}
catch
{
- Console.WriteLine($"Unable to parse stroage reference or connect to storage account {connectionString}.");
+ Console.WriteLine($"Unable to parse stroage reference or connect to storage account {adlsUri}.");
throw;
}
}
@@ -268,11 +555,12 @@ private static void PopulateJobQueue(string resourceType, int unitSize)
{
var startId = LastUpdatedToResourceSurrogateId(StartDate);
var endId = LastUpdatedToResourceSurrogateId(EndDate);
- var resourceTypeId = Store.GetResourceTypeId(resourceType);
- var ranges = Store.GetSurrogateIdRanges(resourceTypeId, startId, endId, unitSize);
- var strings = ranges.Select(_ => $"{_.UnitId};{resourceTypeId};{_.StartId};{_.EndId};{_.ResourceCount}").ToList();
+ var resourceTypeId = Source.GetResourceTypeId(resourceType);
+ var ranges = Source.GetSurrogateIdRanges(resourceTypeId, startId, endId, unitSize, (int)(2e9 / unitSize));
+
+ var strings = ranges.Select(_ => $"{0};{resourceTypeId};{_.StartId};{_.EndId};{0}").ToList();
- var queueConn = new SqlConnection(Store.ConnectionString);
+ var queueConn = new SqlConnection(Source.ConnectionString);
queueConn.Open();
using var drop = new SqlCommand("IF object_id('##StoreCopyWorkQueue') IS NOT NULL DROP TABLE ##StoreCopyWorkQueue", queueConn) { CommandTimeout = 60 };
drop.ExecuteNonQuery();
diff --git a/tools/Exporter/Properties/launchSettings.json b/tools/Exporter/Properties/launchSettings.json
new file mode 100644
index 0000000000..7fe301173c
--- /dev/null
+++ b/tools/Exporter/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Exporter": {
+ "commandName": "Project",
+ "commandLineArgs": "random"
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/Exporter/SqlParamaterStringExtension.cs b/tools/Exporter/SqlParamaterStringExtension.cs
index 4db3e241f6..ca78ac5afd 100644
--- a/tools/Exporter/SqlParamaterStringExtension.cs
+++ b/tools/Exporter/SqlParamaterStringExtension.cs
@@ -5,8 +5,8 @@
using System.Collections.Generic;
using System.Data;
-using System.Data.SqlClient;
-using Microsoft.SqlServer.Server;
+using Microsoft.Data.SqlClient;
+using Microsoft.Data.SqlClient.Server;
namespace Microsoft.Health.Fhir.Store.Export
{
diff --git a/tools/Exporter/SqlService.cs b/tools/Exporter/SqlService.cs
index c829a783fa..b3ec59b86c 100644
--- a/tools/Exporter/SqlService.cs
+++ b/tools/Exporter/SqlService.cs
@@ -4,9 +4,9 @@
// -------------------------------------------------------------------------------------------------
using System.Collections.Generic;
using System.Data;
-using System.Data.SqlClient;
using System.IO;
using System.Threading;
+using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Health.Fhir.Core.Features.Operations;
using Microsoft.Health.Fhir.SqlServer.Features.Storage;
@@ -35,6 +35,12 @@ public SqlService(string connectionString)
public SqlRetryService SqlRetryService => _sqlRetryService;
+ internal string ShowConnectionString()
+ {
+ var builder = new SqlConnectionStringBuilder(_connectionString);
+ return builder.DataSource + "." + builder.InitialCatalog;
+ }
+
internal void DequeueJob(out long groupId, out long jobId, out long version, out short? resourceTypeId, out string minSurIdOrUrl, out string maxSurId)
{
var jobInfo = _queue.DequeueAsync(_queueType, "A", 600, CancellationToken.None).Result;
@@ -62,6 +68,21 @@ internal void CompleteJob(long jobId, bool failed, long version, int? resourceCo
_queue.CompleteJobAsync(jobInfo, false, CancellationToken.None).Wait();
}
+ internal IEnumerable<(long FileId, int OffsetInFile)> GetRefs(short resourceTypeId, long minId, long maxId)
+ {
+ using var conn = new SqlConnection(ConnectionString);
+ conn.Open();
+ using var cmd = new SqlCommand("SELECT FileId, OffsetInFile FROM dbo.Resource WHERE IsHistory = 0 AND ResourceTypeId = @ResourceTypeId AND ResourceSurrogateId BETWEEN @StartId AND @EndId", conn) { CommandTimeout = 600 };
+ cmd.Parameters.AddWithValue("@ResourceTypeId", resourceTypeId);
+ cmd.Parameters.AddWithValue("@StartId", minId);
+ cmd.Parameters.AddWithValue("@EndId", maxId);
+ using var reader = cmd.ExecuteReader();
+ while (reader.Read())
+ {
+ yield return (reader.GetInt64(0), reader.GetInt32(1));
+ }
+ }
+
internal IEnumerable GetDataBytes(short resourceTypeId, long minId, long maxId)
{
using var conn = new SqlConnection(ConnectionString);
@@ -77,7 +98,7 @@ internal IEnumerable GetDataBytes(short resourceTypeId, long minId, long
}
}
- internal IEnumerable<(int UnitId, long StartId, long EndId, int ResourceCount)> GetSurrogateIdRanges(short resourceTypeId, long startId, long endId, int unitSize)
+ internal IEnumerable<(int UnitId, long StartId, long EndId, int ResourceCount)> GetSurrogateIdRanges(short resourceTypeId, long startId, long endId, int unitSize, int numberOfRanges)
{
using var conn = new SqlConnection(ConnectionString);
conn.Open();
@@ -85,8 +106,8 @@ internal IEnumerable GetDataBytes(short resourceTypeId, long minId, long
cmd.Parameters.AddWithValue("@ResourceTypeId", resourceTypeId);
cmd.Parameters.AddWithValue("@StartId", startId);
cmd.Parameters.AddWithValue("@EndId", endId);
- cmd.Parameters.AddWithValue("@UnitSize", unitSize);
- cmd.Parameters.AddWithValue("@NumberOfRanges", (int)(2e9 / unitSize));
+ cmd.Parameters.AddWithValue("@RangeSize", unitSize);
+ cmd.Parameters.AddWithValue("@NumberOfRanges", numberOfRanges);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
@@ -118,5 +139,10 @@ internal IEnumerable GetDataStrings(short resourceTypeId, long minId, lo
yield return Health.Fhir.Store.Export.CompressedRawResourceConverterCopy.ReadCompressedRawResource(mem);
}
}
+
+ internal void LogEvent(string process, string status, string text, DateTime start)
+ {
+ _sqlRetryService.TryLogEvent(process, status, text, start, CancellationToken.None).Wait();
+ }
}
}
diff --git a/tools/PerfTester/App.config b/tools/PerfTester/App.config
index 78dff2f0d6..8e6b6a00fd 100644
--- a/tools/PerfTester/App.config
+++ b/tools/PerfTester/App.config
@@ -7,21 +7,27 @@
-
+
+
+
-
+
-
-
+
+
+
+
@@ -29,8 +35,10 @@
-
-
+
+
+
+
diff --git a/tools/PerfTester/Program.cs b/tools/PerfTester/Program.cs
index b7051cbf4f..88b968c57e 100644
--- a/tools/PerfTester/Program.cs
+++ b/tools/PerfTester/Program.cs
@@ -13,6 +13,7 @@
using System.Security.Cryptography;
using System.Text;
using System.Threading;
+using Azure.Identity;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Specialized;
using Microsoft.Data.SqlClient;
@@ -29,6 +30,8 @@ public static class Program
{
private static readonly string _connectionString = ConfigurationManager.ConnectionStrings["Database"].ConnectionString;
private static readonly string _storageConnectionString = ConfigurationManager.AppSettings["StorageConnectionString"];
+ private static readonly string _storageUri = ConfigurationManager.AppSettings["StorageUri"];
+ private static readonly string _storageUAMI = ConfigurationManager.AppSettings["StorageUAMI"];
private static readonly string _storageContainerName = ConfigurationManager.AppSettings["StorageContainerName"];
private static readonly string _storageBlobName = ConfigurationManager.AppSettings["StorageBlobName"];
private static readonly int _reportingPeriodSec = int.Parse(ConfigurationManager.AppSettings["ReportingPeriodSec"]);
@@ -40,12 +43,15 @@ public static class Program
private static readonly string _endpoint = ConfigurationManager.AppSettings["FhirEndpoint"];
private static readonly HttpClient _httpClient = new HttpClient();
private static readonly string _ndjsonStorageConnectionString = ConfigurationManager.AppSettings["NDJsonStorageConnectionString"];
+ private static readonly string _ndjsonStorageUri = ConfigurationManager.AppSettings["NDJsonStorageUri"];
+ private static readonly string _ndjsonStorageUAMI = ConfigurationManager.AppSettings["NDJsonStorageUAMI"];
private static readonly string _ndjsonStorageContainerName = ConfigurationManager.AppSettings["NDJsonStorageContainerName"];
private static readonly int _takeBlobs = int.Parse(ConfigurationManager.AppSettings["TakeBlobs"]);
private static readonly int _skipBlobs = int.Parse(ConfigurationManager.AppSettings["SkipBlobs"]);
private static readonly string _nameFilter = ConfigurationManager.AppSettings["NameFilter"];
private static readonly bool _writesEnabled = bool.Parse(ConfigurationManager.AppSettings["WritesEnabled"]);
private static readonly int _repeat = int.Parse(ConfigurationManager.AppSettings["Repeat"]);
+ private static readonly int _diagSleepSec = int.Parse(ConfigurationManager.AppSettings["DiagSleepSec"]);
private static SqlRetryService _sqlRetryService;
private static SqlStoreClient _store;
@@ -59,6 +65,12 @@ public static void Main()
DumpResourceIds();
+ if (_callType == "Diag")
+ {
+ Diag();
+ return;
+ }
+
if (_callType == "GetDate" || _callType == "LogEvent")
{
Console.WriteLine($"Start at {DateTime.UtcNow.ToString("s")}");
@@ -101,6 +113,76 @@ public static void Main()
}
}
+ private static void Diag()
+ {
+ Console.WriteLine($"Diag: start at {DateTime.UtcNow.ToString("s")}");
+ var container = GetContainer(_ndjsonStorageConnectionString, _ndjsonStorageUri, _ndjsonStorageUAMI, _ndjsonStorageContainerName);
+ var swTotal = Stopwatch.StartNew();
+ var patients = GetResourceIds("Patient").Select(_ => _.ResourceId).OrderBy(_ => RandomNumberGenerator.GetInt32((int)1e9)).ToList();
+ Console.WriteLine($"Diag: read patient ids = {patients.Count} in {(int)swTotal.Elapsed.TotalSeconds} sec.");
+ swTotal = Stopwatch.StartNew();
+ var observations = GetResourceIds("Observation").Select(_ => _.ResourceId).OrderBy(_ => RandomNumberGenerator.GetInt32((int)1e9)).Take(100000).ToList();
+ Console.WriteLine($"Diag: read observation ids = {observations.Count} in {(int)swTotal.Elapsed.TotalSeconds} sec.");
+ var loop = 0;
+ var observationIndex = 0;
+ var patientIndex = 0;
+ var swReport = Stopwatch.StartNew();
+ swTotal = Stopwatch.StartNew();
+ var observationEnumerator = GetLinesInBlobs(container, "Observation").GetEnumerator();
+ while (true)
+ {
+ var id = observations[observationIndex++];
+ var sw = Stopwatch.StartNew();
+ var status = GetResource("Observation", id);
+ _store.TryLogEvent("Diag.Get.Observation", "Warn", $"{(int)sw.Elapsed.TotalMilliseconds} msec status={status} id={id}", null, CancellationToken.None).Wait();
+
+ id = observations[observationIndex++];
+ var json = observationEnumerator.MoveNext() ? observationEnumerator.Current : throw new ArgumentException("obervation list is too small");
+ ParseJson(ref json, id); // replace id in json
+ sw = Stopwatch.StartNew();
+ status = PutResource(json, "Observation", id);
+ _store.TryLogEvent("Diag.Update.Observation", "Warn", $"{(int)sw.Elapsed.TotalMilliseconds} msec status={status} id={id}", null, CancellationToken.None).Wait();
+
+ id = Guid.NewGuid().ToString();
+ json = observationEnumerator.MoveNext() ? observationEnumerator.Current : throw new ArgumentException("obervation list is too small");
+ ParseJson(ref json, id); // replace id in json
+ sw = Stopwatch.StartNew();
+ status = PutResource(json, "Observation", id);
+ _store.TryLogEvent("Diag.Create.Observation", "Warn", $"{(int)sw.Elapsed.TotalMilliseconds} msec status={status} id={id}", null, CancellationToken.None).Wait();
+
+ id = observations[observationIndex++];
+ DeleteResource("Observation", id, true);
+
+ id = observations[observationIndex++];
+ DeleteResource("Observation", id, false);
+
+ id = observations[observationIndex++];
+ UpdateSearchParam(id);
+
+ id = patients[patientIndex++];
+ GetObservationsForPatient(id);
+
+ id = patients[patientIndex++];
+ GetPatientRevIncludeObservations(id);
+
+ if (swReport.Elapsed.TotalSeconds > _reportingPeriodSec)
+ {
+ lock (swReport)
+ {
+ if (swReport.Elapsed.TotalSeconds > _reportingPeriodSec)
+ {
+ Console.WriteLine($"Diag: loops={loop} elapsed={(int)swTotal.Elapsed.TotalSeconds} sec");
+ swReport.Restart();
+ }
+ }
+ }
+
+ Thread.Sleep(_diagSleepSec * 1000);
+
+ loop++;
+ }
+ }
+
private static ReadOnlyList GetRandomTransactionIds()
{
var sw = Stopwatch.StartNew();
@@ -181,56 +263,64 @@ private static void GetDate()
private static void ExecuteParallelHttpPuts()
{
var resourceIds = _callType == "HttpUpdate" || _callType == "BundleUpdate" ? GetRandomIds() : new List<(short ResourceTypeId, string ResourceId)>();
- var sourceContainer = GetContainer(_ndjsonStorageConnectionString, _ndjsonStorageContainerName);
+ var sourceContainer = GetContainer(_ndjsonStorageConnectionString, _ndjsonStorageUri, _ndjsonStorageUAMI, _ndjsonStorageContainerName);
var tableOrView = GetResourceObjectType();
- var sw = Stopwatch.StartNew();
- var swReport = Stopwatch.StartNew();
- var calls = 0L;
- var resources = 0;
- long sumLatency = 0;
- var singleId = Guid.NewGuid().ToString();
- BatchExtensions.ExecuteInParallelBatches(GetLinesInBlobs(sourceContainer), _threads, 1, (thread, lineItem) =>
+ for (var repeat = 0; repeat < _repeat; repeat++)
{
- if (Interlocked.Read(ref calls) >= _calls)
+ var sw = Stopwatch.StartNew();
+ var swReport = Stopwatch.StartNew();
+ var calls = 0L;
+ var errors = 0L;
+ var resources = 0;
+ long sumLatency = 0;
+ var singleId = Guid.NewGuid().ToString();
+ BatchExtensions.ExecuteInParallelBatches(GetLinesInBlobs(sourceContainer, _nameFilter), _threads, 1, (thread, lineItem) =>
{
- return;
- }
+ if (Interlocked.Read(ref calls) >= _calls)
+ {
+ return;
+ }
- var callId = (int)Interlocked.Increment(ref calls) - 1;
- if ((_callType == "HttpUpdate" || _callType == "BundleUpdate") && callId >= resourceIds.Count)
- {
- return;
- }
+ var callId = (int)Interlocked.Increment(ref calls) - 1;
+ if ((_callType == "HttpUpdate" || _callType == "BundleUpdate") && callId >= resourceIds.Count)
+ {
+ return;
+ }
- var resourceIdInput = _callType == "SingleId"
- ? singleId
- : _callType == "HttpUpdate" || _callType == "BundleUpdate"
- ? resourceIds[callId].ResourceId
- : Guid.NewGuid().ToString();
+ var resourceIdInput = _callType == "SingleId"
+ ? singleId
+ : _callType == "HttpUpdate" || _callType == "BundleUpdate"
+ ? resourceIds[callId].ResourceId
+ : Guid.NewGuid().ToString();
- var swLatency = Stopwatch.StartNew();
- var json = lineItem.Item2.First();
- var (resourceType, resourceId) = ParseJson(ref json, resourceIdInput);
- var status = _callType == "BundleUpdate" ? PostBundle(json, resourceType, resourceId) : PutResource(json, resourceType, resourceId);
- Interlocked.Increment(ref resources);
- var mcsec = (long)Math.Round(swLatency.Elapsed.TotalMilliseconds * 1000, 0);
- Interlocked.Add(ref sumLatency, mcsec);
- _store.TryLogEvent($"{tableOrView}.threads={_threads}.Put:{status}:{resourceType}/{resourceId}", "Warn", $"mcsec={mcsec}", null, CancellationToken.None).Wait();
+ var swLatency = Stopwatch.StartNew();
+ var json = lineItem.Item2.First();
+ var (resourceType, resourceId) = ParseJson(ref json, resourceIdInput);
+ var status = _callType == "BundleUpdate" ? PostBundle(json, resourceType, resourceId) : PutResource(json, resourceType, resourceId);
+ Interlocked.Increment(ref resources);
+ var mcsec = (long)Math.Round(swLatency.Elapsed.TotalMilliseconds * 1000, 0);
+ Interlocked.Add(ref sumLatency, mcsec);
+ _store.TryLogEvent($"{tableOrView}.threads={_threads}.Put:{status}:{resourceType}/{resourceId}", "Warn", $"mcsec={mcsec}", null, CancellationToken.None).Wait();
+ if (_callType != "BundleUpdate" && status != "OK" && status != "Created")
+ {
+ Interlocked.Increment(ref errors);
+ }
- if (swReport.Elapsed.TotalSeconds > _reportingPeriodSec)
- {
- lock (swReport)
+ if (swReport.Elapsed.TotalSeconds > _reportingPeriodSec)
{
- if (swReport.Elapsed.TotalSeconds > _reportingPeriodSec)
+ lock (swReport)
{
- Console.WriteLine($"{tableOrView} type={_callType} writes={_writesEnabled} threads={_threads} calls={calls} resources={resources} latency={sumLatency / 1000.0 / calls} ms speed={(int)(calls / sw.Elapsed.TotalSeconds)} calls/sec elapsed={(int)sw.Elapsed.TotalSeconds} sec");
- swReport.Restart();
+ if (swReport.Elapsed.TotalSeconds > _reportingPeriodSec)
+ {
+ Console.WriteLine($"{tableOrView} type={_callType} writes={_writesEnabled} threads={_threads} calls={calls} errors={errors} resources={resources} latency={sumLatency / 1000.0 / calls} ms speed={(int)(calls / sw.Elapsed.TotalSeconds)} calls/sec elapsed={(int)sw.Elapsed.TotalSeconds} sec");
+ swReport.Restart();
+ }
}
}
- }
- });
+ });
- Console.WriteLine($"{tableOrView} type={_callType} writes={_writesEnabled} threads={_threads} calls={calls} resources={resources} latency={sumLatency / 1000.0 / calls} ms speed={(int)(calls / sw.Elapsed.TotalSeconds)} calls/sec elapsed={(int)sw.Elapsed.TotalSeconds} sec");
+ Console.WriteLine($"{tableOrView} type={_callType} writes={_writesEnabled} threads={_threads} calls={calls} errors={errors} resources={resources} latency={sumLatency / 1000.0 / calls} ms speed={(int)(calls / sw.Elapsed.TotalSeconds)} calls/sec elapsed={(int)sw.Elapsed.TotalSeconds} sec");
+ }
}
private static void ExecuteParallelCalls(ReadOnlyList tranIds)
@@ -309,7 +399,7 @@ private static void ExecuteParallelCalls(ReadOnlyList<(short ResourceTypeId, str
}
else if (_callType.StartsWith("SearchByIds"))
{
- var status = GetResources(_nameFilter, resourceIds.Item2.Select(_ => _.ResourceId)?.ToList());
+ var status = GetResources(_nameFilter, resourceIds.Item2.Select(_ => _.ResourceId));
if (status != "OK")
{
Interlocked.Increment(ref errors);
@@ -324,13 +414,13 @@ private static void ExecuteParallelCalls(ReadOnlyList<(short ResourceTypeId, str
Interlocked.Increment(ref errors);
}
}
- else if (_callType == "HardDeleteNoChangeCapture")
+ else if (_callType == "HardDeleteNoInvisible")
{
var typeId = resourceIds.Item2.First().ResourceTypeId;
var id = resourceIds.Item2.First().ResourceId;
_store.HardDeleteAsync(typeId, id, false, false, CancellationToken.None).Wait();
}
- else if (_callType == "HardDeleteWithChangeCapture")
+ else if (_callType == "HardDeleteWithInvisible")
{
var typeId = resourceIds.Item2.First().ResourceTypeId;
var id = resourceIds.Item2.First().ResourceId;
@@ -341,7 +431,9 @@ private static void ExecuteParallelCalls(ReadOnlyList<(short ResourceTypeId, str
throw new NotImplementedException();
}
- Interlocked.Add(ref sumLatency, (long)Math.Round(swLatency.Elapsed.TotalMilliseconds * 1000, 0));
+ var mcsec = (long)Math.Round(swLatency.Elapsed.TotalMilliseconds * 1000, 0);
+ Interlocked.Add(ref sumLatency, mcsec);
+ _store.TryLogEvent($"Threads={_threads}.{_callType}.{_nameFilter}", "Warn", $"mcsec={mcsec}", null, CancellationToken.None).Wait();
if (swReport.Elapsed.TotalSeconds > _reportingPeriodSec)
{
@@ -502,7 +594,7 @@ private static void DumpResourceIds()
var container = GetContainer();
using var stream = container.GetBlockBlobClient(_storageBlobName).OpenWrite(true);
using var writer = new StreamWriter(stream);
- foreach (var resourceId in GetResourceIds())
+ foreach (var resourceId in GetResourceIds(_nameFilter))
{
lines++;
writer.WriteLine($"{resourceId.ResourceTypeId}\t{resourceId.ResourceId}");
@@ -525,16 +617,16 @@ private static void DumpResourceIds()
}
// cannot use sqlRetryService as I need IEnumerable
- private static IEnumerable<(short ResourceTypeId, string ResourceId)> GetResourceIds()
+ private static IEnumerable<(short ResourceTypeId, string ResourceId)> GetResourceIds(string nameFilter)
{
using var conn = new SqlConnection(_connectionString);
conn.Open();
using var cmd = new SqlCommand("SELECT ResourceTypeId FROM dbo.ResourceType WHERE Name = @Name", conn);
- cmd.Parameters.AddWithValue("@Name", _nameFilter);
+ cmd.Parameters.AddWithValue("@Name", nameFilter);
var ret = cmd.ExecuteScalar();
if (ret == DBNull.Value)
{
- using var cmd2 = new SqlCommand("SELECT ResourceTypeId, ResourceId FROM dbo.Resource WHERE IsHistory = 0 ORDER BY ResourceTypeId, ResourceId OPTION (MAXDOP 1)", conn);
+ using var cmd2 = new SqlCommand("SELECT ResourceTypeId, ResourceId FROM dbo.Resource WHERE IsHistory = 0 AND IsDeleted = 0", conn); // no need to sort to simulate random access
cmd2.CommandTimeout = 0;
using var reader = cmd2.ExecuteReader();
while (reader.Read())
@@ -545,7 +637,7 @@ private static void DumpResourceIds()
else
{
var resourceTypeId = (short)ret;
- using var cmd2 = new SqlCommand("SELECT ResourceTypeId, ResourceId FROM dbo.Resource WITH (INDEX = IX_Resource_ResourceTypeId_ResourceId) WHERE IsHistory = 0 AND ResourceTypeId = @ResourceTypeId ORDER BY ResourceTypeId, ResourceId OPTION (MAXDOP 1)", conn);
+ using var cmd2 = new SqlCommand("SELECT ResourceTypeId, ResourceId FROM dbo.Resource WHERE IsHistory = 0 AND IsDeleted = 0 AND ResourceTypeId = @ResourceTypeId OPTION (LOOP JOIN)", conn); // no need to sort to simulate random access
cmd2.Parameters.AddWithValue("@ResourceTypeId", resourceTypeId);
cmd2.CommandTimeout = 0;
using var reader = cmd2.ExecuteReader();
@@ -566,14 +658,14 @@ private static string GetResourceObjectType()
private static BlobContainerClient GetContainer()
{
- return GetContainer(_storageConnectionString, _storageContainerName);
+ return GetContainer(_storageConnectionString, _storageUri, _storageUAMI, _storageContainerName);
}
- private static BlobContainerClient GetContainer(string storageConnectionString, string storageContainerName)
+ private static BlobContainerClient GetContainer(string storageConnectionString, string storageUri, string storageUAMI, string storageContainerName)
{
try
{
- var blobServiceClient = new BlobServiceClient(storageConnectionString);
+ var blobServiceClient = string.IsNullOrEmpty(storageUri) ? new BlobServiceClient(storageConnectionString) : new BlobServiceClient(new Uri(storageUri), string.IsNullOrEmpty(storageUAMI) ? new InteractiveBrowserCredential() : new ManagedIdentityCredential(storageUAMI));
var blobContainerClient = blobServiceClient.GetBlobContainerClient(storageContainerName);
if (!blobContainerClient.Exists())
@@ -591,10 +683,10 @@ private static BlobContainerClient GetContainer(string storageConnectionString,
}
}
- private static IEnumerable GetLinesInBlobs(BlobContainerClient container)
+ private static IEnumerable GetLinesInBlobs(BlobContainerClient container, string nameFilter)
{
var linesRead = 0;
- var blobs = container.GetBlobs().Where(_ => _.Name.Contains(_nameFilter, StringComparison.OrdinalIgnoreCase)).Skip(_skipBlobs).Take(_takeBlobs);
+ var blobs = container.GetBlobs().Where(_ => _.Name.Contains(nameFilter, StringComparison.OrdinalIgnoreCase)).Skip(_skipBlobs).Take(_takeBlobs);
foreach (var blob in blobs)
{
if (linesRead >= _calls)
@@ -793,7 +885,7 @@ private static string PostBundle(string jsonString, string resourceType, string
return status;
}
- private static string GetResources(string resourceType, System.Collections.Generic.IReadOnlyCollection resourceIds)
+ private static string GetResources(string resourceType, IEnumerable resourceIds)
{
var maxRetries = 3;
var retries = 0;
@@ -923,6 +1015,140 @@ private static string GetResource(string resourceType, string resourceId)
return status;
}
+ private static void GetObservationsForPatient(string patientId)
+ {
+ var sw = Stopwatch.StartNew();
+ var status = string.Empty;
+ var uri = new Uri(_endpoint + "/Observation?patient=" + patientId);
+ var count = 0;
+ try
+ {
+ var response = _httpClient.GetAsync(uri).Result;
+ status = response.StatusCode.ToString();
+ var content = response.Content.ReadAsStringAsync().Result;
+ var split = content.Split("fullUrl", StringSplitOptions.None);
+ count = split.Length - 1;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"uri={uri} error={e.Message}");
+ _store.TryLogEvent("Diag.GetObservationsForPatient", "Error", $"id={patientId} error={e.Message}", null, CancellationToken.None).Wait();
+ }
+
+ _store.TryLogEvent("Diag.GetObservationsForPatient", "Warn", $"{(int)sw.Elapsed.TotalMilliseconds} msec status={status} count={count} id={patientId}", null, CancellationToken.None).Wait();
+ }
+
+ private static void GetPatientRevIncludeObservations(string patientId)
+ {
+ var sw = Stopwatch.StartNew();
+ var status = string.Empty;
+ var uri = new Uri(_endpoint + "/Patient?_revinclude=Observation:patient&_id=" + patientId);
+ var count = 0;
+ try
+ {
+ var response = _httpClient.GetAsync(uri).Result;
+ status = response.StatusCode.ToString();
+ var content = response.Content.ReadAsStringAsync().Result;
+ var split = content.Split("fullUrl", StringSplitOptions.None);
+ count = split.Length - 1;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"uri={uri} error={e.Message}");
+ _store.TryLogEvent("Diag.GetPatientRevIncludeObservations", "Error", $"id={patientId} error={e.Message}", null, CancellationToken.None).Wait();
+ }
+
+ _store.TryLogEvent("Diag.GetPatientRevIncludeObservations", "Warn", $"{(int)sw.Elapsed.TotalMilliseconds} msec status={status} count={count} id={patientId}", null, CancellationToken.None).Wait();
+ }
+
+ private static void UpdateSearchParam(string resourceId)
+ {
+ var sw = Stopwatch.StartNew();
+ try
+ {
+ using var conn = new SqlConnection(_connectionString);
+ conn.Open();
+ using var cmd = new SqlCommand(
+ @"
+ DECLARE @Resources dbo.ResourceList
+ INSERT INTO @Resources
+ ( ResourceTypeId, ResourceId, RawResource, ResourceSurrogateId, Version, HasVersionToCompare, IsDeleted, IsHistory, KeepHistory, IsRawResourceMetaSet, SearchParamHash)
+ SELECT ResourceTypeId, ResourceId, 0x0, ResourceSurrogateId, Version, 1, 0, 0, 1, 1, 'Test'
+ FROM Resource
+ WHERE ResourceTypeId = 96 AND ResourceId = @ResourceId
+ EXECUTE UpdateResourceSearchParams @Resources = @Resources
+ ",
+ conn);
+ cmd.Parameters.AddWithValue("@ResourceId", resourceId);
+ cmd.ExecuteNonQuery();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Diag.UpdateSearchParams.Observation: Id={resourceId} error={e.Message}");
+ _store.TryLogEvent($"Diag.UpdateSearchParams.Observation", "Error", $"id={resourceId} error={e.Message}", null, CancellationToken.None).Wait();
+ }
+
+ _store.TryLogEvent($"Diag.UpdateSearchParams.Observation", "Warn", $"{(int)sw.Elapsed.TotalMilliseconds} msec id={resourceId}", null, CancellationToken.None).Wait();
+
+ // check
+ try
+ {
+ using var conn = new SqlConnection(_connectionString);
+ conn.Open();
+ using var cmd = new SqlCommand("SELECT SearchParamHash FROM Resource WHERE ResourceTypeId = 96 AND IsHistory = 0 AND ResourceId = @ResourceId", conn);
+ cmd.Parameters.AddWithValue("@ResourceId", resourceId);
+ var hash = (string)cmd.ExecuteScalar();
+ if (hash != "Test")
+ {
+ throw new ArgumentException("Incorrect hash");
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Diag.Check.UpdateSearchParams.Observation: Id={resourceId} error={e.Message}");
+ _store.TryLogEvent($"Diag.Check.UpdateSearchParams.Observation", "Error", $"id={resourceId} error={e.Message}", null, CancellationToken.None).Wait();
+ }
+ }
+
+ private static void DeleteResource(string resourceType, string resourceId, bool isHard)
+ {
+ var sw = Stopwatch.StartNew();
+ var status = string.Empty;
+ var uri = new Uri(_endpoint + "/" + resourceType + "/" + resourceId + (isHard ? "?hardDelete=true" : string.Empty));
+ try
+ {
+ var response = _httpClient.DeleteAsync(uri).Result;
+ status = response.StatusCode.ToString();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"uri={uri} error={e.Message}");
+ _store.TryLogEvent($"Diag.{(isHard ? "Hard" : "Soft")}Delete.Observation", "Error", $"id={resourceId} error={e.Message}", null, CancellationToken.None).Wait();
+ }
+
+ _store.TryLogEvent($"Diag.{(isHard ? "Hard" : "Soft")}Delete.Observation", "Warn", $"{(int)sw.Elapsed.TotalMilliseconds} msec status={status} id={resourceId}", null, CancellationToken.None).Wait();
+
+ // check
+ sw = Stopwatch.StartNew();
+ uri = new Uri(_endpoint + "/" + resourceType + "/" + resourceId + "/_history");
+ var count = 0;
+ try
+ {
+ var response = _httpClient.GetAsync(uri).Result;
+ status = response.StatusCode.ToString();
+ var content = response.Content.ReadAsStringAsync().Result;
+ var split = content.Split("fullUrl", StringSplitOptions.None);
+ count = split.Length - 1;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"uri={uri} error={e.Message}");
+ _store.TryLogEvent($"Diag.Check{(isHard ? "Hard" : "Soft")}Delete.Observation", "Error", $"id={resourceId} error={e.Message}", null, CancellationToken.None).Wait();
+ }
+
+ _store.TryLogEvent($"Diag.Check{(isHard ? "Hard" : "Soft")}Delete.Observation", "Warn", $"{(int)sw.Elapsed.TotalMilliseconds} msec count={count} status={status} id={resourceId}", null, CancellationToken.None).Wait();
+ }
+
private static bool IsNetworkError(Exception e)
{
return e.Message.Contains("connection attempt failed", StringComparison.OrdinalIgnoreCase)