/// <summary> /// This method contains the common functionality for updating the hash-index bucket. /// </summary> /// <typeparam name="K">key type</typeparam> /// <typeparam name="V">value type</typeparam> /// <param name="updatedGrain">the updated grain that is being indexed</param> /// <param name="update">the update information</param> /// <param name="state">the index bucket to be updated</param> /// <param name="isUniqueIndex">a flag to indicate whether the hash-index has a uniqueness constraint</param> /// <param name="idxMetaData">the index metadata</param> /// <param name="fixIndexUnavailableOnDelete">output parameter: this variable determines whether /// the index was still unavailable when we received a delete operation</param> internal static bool UpdateBucketState <K, V>(V updatedGrain, IMemberUpdate update, HashIndexBucketState <K, V> state, bool isUniqueIndex, IndexMetaData idxMetaData, out bool fixIndexUnavailableOnDelete) where V : IIndexableGrain { fixIndexUnavailableOnDelete = false; var indexUpdateMode = update.UpdateMode; var opType = update.OperationType; HashIndexSingleBucketEntry <V> aftEntry; // Insert is done for both IndexOperationType.Update and IndexOperationType.Update, so use a local function. bool doInsert(K afterImage, out bool uniquenessViolation) { uniquenessViolation = false; if (state.IndexMap.TryGetValue(afterImage, out aftEntry)) { if (!aftEntry.Values.Contains(updatedGrain)) { if (isUniqueIndex && aftEntry.Values.Count > 0) { uniquenessViolation = true; return(false); } aftEntry.Add(updatedGrain, indexUpdateMode, isUniqueIndex); } else if (indexUpdateMode == IndexUpdateMode.Tentative) { aftEntry.SetTentativeInsert(); } else { aftEntry.ClearTentativeFlag(); } return(true); } // Add a new entry if (idxMetaData.IsCreatingANewBucketNecessary(state.IndexMap.Count)) { return(false); // the bucket is full } aftEntry = new HashIndexSingleBucketEntry <V>(); aftEntry.Add(updatedGrain, indexUpdateMode, isUniqueIndex); state.IndexMap.Add(afterImage, aftEntry); return(true); } if (opType == IndexOperationType.Update) { K aftImg = (K)update.GetAfterImage(); var befImg = (K)update.GetBeforeImage(); if (state.IndexMap.TryGetValue(befImg, out var befEntry) && befEntry.Values.Contains(updatedGrain)) { // Delete and Insert if (state.IndexMap.TryGetValue(aftImg, out aftEntry)) { if (aftEntry.Values.Contains(updatedGrain)) { if (indexUpdateMode == IndexUpdateMode.Tentative) { aftEntry.SetTentativeInsert(); } else { aftEntry.ClearTentativeFlag(); befEntry.Remove(updatedGrain, indexUpdateMode, isUniqueIndex); } } else { if (isUniqueIndex && aftEntry.Values.Count > 0) { throw new UniquenessConstraintViolatedException( $"The uniqueness property of index {idxMetaData.IndexName} is would be violated for an update operation" + $" for before-image = {befImg}, after-image = {aftImg} and grain = {updatedGrain.GetPrimaryKey()}"); } befEntry.Remove(updatedGrain, indexUpdateMode, isUniqueIndex); aftEntry.Add(updatedGrain, indexUpdateMode, isUniqueIndex); } } else { aftEntry = new HashIndexSingleBucketEntry <V>(); befEntry.Remove(updatedGrain, indexUpdateMode, isUniqueIndex); aftEntry.Add(updatedGrain, indexUpdateMode, isUniqueIndex); state.IndexMap.Add(aftImg, aftEntry); } } else { // Insert-only, because Delete was not found. If there's a NextBucket so return false to search it; // otherwise, the desired Delete result is met (the previous value is not present), so proceed to Insert. if (idxMetaData.IsChainedBuckets && state.NextBucket != null) { return(false); } if (!doInsert(aftImg, out bool uniquenessViolation)) { return(uniquenessViolation ? throw new UniquenessConstraintViolatedException( $"The uniqueness property of index {idxMetaData.IndexName} would be violated for an update operation" + $" for (not found before-image = {befImg}), after-image = {aftImg} and grain = {updatedGrain.GetPrimaryKey()}") : false); // The bucket is full } } } else if (opType == IndexOperationType.Insert) { if (!doInsert((K)update.GetAfterImage(), out bool uniquenessViolation)) { return(uniquenessViolation ? throw new UniquenessConstraintViolatedException( $"The uniqueness property of index {idxMetaData.IndexName} would be violated for an insert operation" + $" for after-image = {(K)update.GetAfterImage()} and grain = {updatedGrain.GetPrimaryKey()}") : false); } } else if (opType == IndexOperationType.Delete) { var befImg = (K)update.GetBeforeImage(); if (state.IndexMap.TryGetValue(befImg, out var befEntry) && befEntry.Values.Contains(updatedGrain)) { befEntry.Remove(updatedGrain, indexUpdateMode, isUniqueIndex); if (state.IndexStatus != IndexStatus.Available) { fixIndexUnavailableOnDelete = true; } } else if (idxMetaData.IsChainedBuckets) { // Not found in this bucket. If there's a NextBucket, return false to search it; // otherwise, the desired Delete result is met (the value is not present), so return true. return(state.NextBucket == null); } } return(true); }
/// <summary> /// This method contains the common functionality for updating /// hash-index bucket. /// </summary> /// <typeparam name="K">key type</typeparam> /// <typeparam name="V">value type</typeparam> /// <param name="updatedGrain">the updated grain that is being indexed</param> /// <param name="update">the update information</param> /// <param name="opType">the update operation type, which might be different /// from the update operation type inside the update parameter</param> /// <param name="State">the index bucket to be updated</param> /// <param name="isUniqueIndex">a flag to indicate whether the /// hash-index has a uniqueness constraint</param> /// <param name="befImg">output parameter: the before-image</param> /// <param name="befEntry">output parameter: the index entry containing the before-image</param> /// <param name="fixIndexUnavailableOnDelete">output parameter: this variable determines whether /// index was still unavailable when we received a delete operation</param> internal static bool UpdateBucket <K, V>(V updatedGrain, IMemberUpdate update, HashIndexBucketState <K, V> State, bool isUniqueIndex, IndexMetaData idxMetaData, out K befImg, out HashIndexSingleBucketEntry <V> befEntry, out bool fixIndexUnavailableOnDelete) where V : IIndexableGrain { fixIndexUnavailableOnDelete = false; befImg = default(K); befEntry = null; bool isTentativeUpdate = isUniqueIndex && (update is MemberUpdateTentative); IndexOperationType opType = update.GetOperationType(); HashIndexSingleBucketEntry <V> aftEntry; if (opType == IndexOperationType.Update) { befImg = (K)update.GetBeforeImage(); K aftImg = (K)update.GetAfterImage(); if (State.IndexMap.TryGetValue(befImg, out befEntry) && befEntry.Values.Contains(updatedGrain)) { //Delete and Insert if (State.IndexMap.TryGetValue(aftImg, out aftEntry)) { if (aftEntry.Values.Contains(updatedGrain)) { if (isTentativeUpdate) { aftEntry.setTentativeInsert(); } else { aftEntry.clearTentativeFlag(); befEntry.Remove(updatedGrain, isTentativeUpdate, isUniqueIndex); } } else { if (isUniqueIndex && aftEntry.Values.Count > 0) { throw new UniquenessConstraintViolatedException(string.Format("The uniqueness property of index is violated after an update operation for before-image = {0}, after-image = {1} and grain = {2}", befImg, aftImg, updatedGrain.GetPrimaryKey())); } befEntry.Remove(updatedGrain, isTentativeUpdate, isUniqueIndex); aftEntry.Add(updatedGrain, isTentativeUpdate, isUniqueIndex); } } else { aftEntry = new HashIndexSingleBucketEntry <V>(); befEntry.Remove(updatedGrain, isTentativeUpdate, isUniqueIndex); aftEntry.Add(updatedGrain, isTentativeUpdate, isUniqueIndex); State.IndexMap.Add(aftImg, aftEntry); } } else { if (idxMetaData.IsChainedBuckets()) { return(false); //not found in this bucket } //Insert if (State.IndexMap.TryGetValue(aftImg, out aftEntry)) { if (!aftEntry.Values.Contains(updatedGrain)) { if (isUniqueIndex && aftEntry.Values.Count > 0) { throw new UniquenessConstraintViolatedException(string.Format("The uniqueness property of index is violated after an update operation for (not found before-image = {0}), after-image = {1} and grain = {2}", befImg, aftImg, updatedGrain.GetPrimaryKey())); } aftEntry.Add(updatedGrain, isTentativeUpdate, isUniqueIndex); } else if (isTentativeUpdate) { aftEntry.setTentativeInsert(); } else { aftEntry.clearTentativeFlag(); } } else { if (idxMetaData.IsCreatingANewBucketNecessary(State.IndexMap.Count())) { return(false); } aftEntry = new HashIndexSingleBucketEntry <V>(); aftEntry.Add(updatedGrain, isTentativeUpdate, isUniqueIndex); State.IndexMap.Add(aftImg, aftEntry); } } } else if (opType == IndexOperationType.Insert) { // Insert K aftImg = (K)update.GetAfterImage(); if (State.IndexMap.TryGetValue(aftImg, out aftEntry)) { if (!aftEntry.Values.Contains(updatedGrain)) { if (isUniqueIndex && aftEntry.Values.Count > 0) { throw new UniquenessConstraintViolatedException(string.Format("The uniqueness property of index is violated after an insert operation for after-image = {0} and grain = {1}", aftImg, updatedGrain.GetPrimaryKey())); } aftEntry.Add(updatedGrain, isTentativeUpdate, isUniqueIndex); } else if (isTentativeUpdate) { aftEntry.setTentativeInsert(); } else { aftEntry.clearTentativeFlag(); } } else { if (idxMetaData.IsCreatingANewBucketNecessary(State.IndexMap.Count())) { return(false); //the bucket is full } aftEntry = new HashIndexSingleBucketEntry <V>(); aftEntry.Add(updatedGrain, isTentativeUpdate, isUniqueIndex); State.IndexMap.Add(aftImg, aftEntry); } } else if (opType == IndexOperationType.Delete) { // Delete befImg = (K)update.GetBeforeImage(); if (State.IndexMap.TryGetValue(befImg, out befEntry) && befEntry.Values.Contains(updatedGrain)) { befEntry.Remove(updatedGrain, isTentativeUpdate, isUniqueIndex); if (State.IndexStatus != IndexStatus.Available) { fixIndexUnavailableOnDelete = true; } } else if (idxMetaData.IsChainedBuckets()) { return(false); //not found in this bucket } } return(true); }