private void CheckTypeDetermination(string numericColumnTypeName) { // Create a numeric column with 0-10 in it IUntypedColumn column = ColumnFactory.Build(new ColumnDetails("Unused", numericColumnTypeName, 10), 0); column.SetSize(10); for (int i = 0; i < 10; ++i) { column[(ushort)i] = i; } // Include 0, 2, 4, 6, 8 in the results ShortSet matches = new ShortSet(10); for (int i = 0; i < 10; i += 2) { matches.Add((ushort)i); } // Ask for the Min and verify both the value and type are correct // This verifies the type checks in BaseAggregator.Aggregate determine type correctly MinAggregator aggregator = new MinAggregator(); object context = aggregator.CreateContext(); object result = aggregator.Aggregate(context, matches, new IUntypedColumn[] { column }); Assert.AreEqual(column[0], result); }
/// <summary> /// Change the type of a column to a new type. Values are copied from the existing /// column to the new one, if value conversion is possible. /// </summary> /// <param name="details">Details with existing name and new other details</param> public void AlterColumn(ColumnDetails details) { if (details == null) { throw new ArgumentNullException("details"); } if (!this.Columns.ContainsKey(details.Name)) { throw new ArribaException(StringExtensions.Format("Column '{0}' does not exist; it can't be altered.", details.Name)); } // Get the old column and build the new one IUntypedColumn currentcolumn = this.Columns[details.Name]; IUntypedColumn replacementColumn = ColumnFactory.Build(details, currentcolumn.Count); // Size the new column and copy each value to it ushort count = this.Count; replacementColumn.SetSize(count); for (ushort i = 0; i < count; ++i) { replacementColumn[i] = currentcolumn[i]; } // Store the new column this.Columns[details.Name] = replacementColumn; this.DetailsByColumn[details.Name] = details; }
/// <summary> /// Add a new column with the given details. Columns must be added before values can be set on them. /// </summary> /// <param name="details">Details of the column to add</param> /// <param name="initialCapacity">suggested initial capacity of the column</param> public void AddColumn(ColumnDetails details, ushort initialCapacity) { if (details == null) { throw new ArgumentNullException("details"); } if (this.Columns.ContainsKey(details.Name)) { if (!this.DetailsByColumn[details.Name].Type.Equals(details.Type)) { AlterColumn(details); return; } // If the column exists and type matches, we can only update side details (alias) this.DetailsByColumn[details.Name] = details; } else { if (details.IsPrimaryKey) { ColumnDetails idColumnDetails = this.IDColumn; if (idColumnDetails != null) { throw new ArribaException(StringExtensions.Format("Column '{0}' to be added is marked as the primary key but cannot be added because column '{1}' is already the primary key column.", details.Name, idColumnDetails.Name)); } } IUntypedColumn newColumn = ColumnFactory.Build(details, initialCapacity); this.Columns[details.Name] = newColumn; this.DetailsByColumn[details.Name] = details; newColumn.SetSize(_itemCount); } }
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); }