public ushort[] FindOrAssignLIDs(Partition p, DataBlock.ReadOnlyDataBlock values, int idColumnIndex, AddOrUpdateMode mode) { // TODO: consider keeping one instance of the worker long term? if so, this becomes a private class field ValueTypeReference <T> vtr = new ValueTypeReference <T>(); Value v = Value.Create(null); ushort[] itemLIDs = new ushort[values.RowCount]; int addCount = 0; IUntypedColumn idColumn = p.Columns[p.IDColumn.Name]; IColumn <T> typedIdColumn = null; if (typeof(T) == idColumn.ColumnType) { typedIdColumn = (IColumn <T>)idColumn.InnerColumn; } for (int index = 0; index < values.RowCount; ++index) { // Look for the LIDs a T externalID = values.GetValueT <T>(index, idColumnIndex); if (typedIdColumn != null) { typedIdColumn.TryGetIndexOf(externalID, out itemLIDs[index]); } else { idColumn.TryGetIndexOf(externalID, out itemLIDs[index]); } if (itemLIDs[index] == ushort.MaxValue) { addCount++; } // Verify this item was routed to the right partition vtr.Value = externalID; v.Assign(vtr); int idHash = v.GetHashCode(); if (!p.Mask.Matches(idHash)) { throw new ArribaException(StringExtensions.Format("Item with ID '{0}', hash '{1:x}' incorrectly routed to Partition {2}.", externalID, idHash, p.Mask)); } } // Go back and add the items which need to be added in a batch if (mode != AddOrUpdateMode.UpdateAndIgnoreAdds) { Dictionary <T, ushort> newlyAssignedLIDs = null; for (int index = 0; index < values.RowCount; ++index) { T idValue = values.GetValueT <T>(index, idColumnIndex); ushort lid = itemLIDs[index]; // If this is an add... if (lid == ushort.MaxValue) { // If we have adds, we'll need to track new IDs if (newlyAssignedLIDs == null) { newlyAssignedLIDs = new Dictionary <T, ushort>(addCount); } T externalID = idValue; // If this ID was already added in this batch, this time it's an update if (newlyAssignedLIDs.TryGetValue(externalID, out lid) == false) { // If in "UpdateOnly" mode, throw if (mode == AddOrUpdateMode.UpdateOnly) { throw new ArribaWriteException(externalID, p.IDColumn.Name, externalID, new ArribaException("AddOrUpdate was in UpdateOnly mode but contained a new ID, which is an error.")); } // If this was a new item and not added in this batch, assign it a LID lid = p._itemCount; if (lid == ushort.MaxValue) { throw new ArribaWriteException(externalID, p.IDColumn.Name, externalID, new ArribaException("Column full in Partition. Unable to add items.")); } p._itemCount++; idColumn.SetSize((ushort)(p._itemCount)); if (typedIdColumn != null) { typedIdColumn[lid] = externalID; } else { idColumn[lid] = externalID; } newlyAssignedLIDs[externalID] = lid; } } itemLIDs[index] = lid; } // Commit the updates to the values column if the column requires it (FastAddSortedColumn does) if (idColumn is ICommittable) { (idColumn as ICommittable).Commit(); } } return(itemLIDs); }
private ushort[] FindOrAssignLIDs(DataBlock.ReadOnlyDataBlock values, int idColumnIndex, AddOrUpdateMode mode) { Type idColumnDataType = values.GetTypeForColumn(idColumnIndex); // If the insert array matches types with the column then we can use the native type to do a direct assignment from the input array // to the column array. If the types do not match, we need to fallback to object to allow the Value class to handle the type conversion ITypedAddOrUpdateWorker worker = NativeContainer.CreateTypedInstance <ITypedAddOrUpdateWorker>(typeof(AddOrUpdateWorker <>), idColumnDataType); return(worker.FindOrAssignLIDs(this, values, idColumnIndex, mode)); }