public bool RemoveNonReachableRows()
        {
            if (_reachableCount == _initialCount)
            {
                return(false);
            }

            if (!_databaseCollector.MaintainObjectModel)
            {
                // Clean up all non-reachable rows
                GarbageCollector.Collect(_table, _refsToTable.Select((c) => c.Column), _reachableRows);
            }
            else
            {
                IDatabase database = _databaseCollector.Database;

                // Construct 'successor' Table with the same columns
                ITable successor = (ITable)Activator.CreateInstance(_table.GetType(), database, _table.Columns);

                // Update the database to refer to the successor table (in new object model instances returned)
                database.Tables[_tableName] = successor;
                database.GetOrBuildTables();

                // Find the 'temp' copy of this table which unreachable rows were copied to
                ITable temp = _databaseCollector.TempDatabase.Tables[_tableName];

                // Build a RowUpdater to redirect object model instances to the successor or temp tables
                RowUpdater updater = new RowUpdater(successor, temp);

                // Clean up all non-reachable rows, and fill out the updater with where to redirect them
                GarbageCollector.Collect(_table, _refsToTable.Select((c) => c.Column), _reachableRows, updater, _rowIndexToTempIndex);

                // Tell the previous table to redirect object model objects
                _table.Updater = updater;

                // Tell the successor table about the new row count (Collect removes happened after it was created)
                successor.SetCount(_table.Count);
            }

            return(true);
        }
Example #2
0
        /// <summary>
        ///  Garbage Collect a column (or table) given a pre-filled set of rowsToKeep.
        ///  Used both to clean up unused values in columns, and to clean up unused rows in tables.
        ///
        ///   - Swap and Remove unused rows in the column itself.
        ///   - Ensure no row is ever swapped more than once.
        ///   - Update references in all 'refsToColumn' to new indices for all swapped rows.
        ///   - Populate RowUpdater, if passed, with new indices for swapped and kept rows.
        ///   - Populate RowUpdater, if passed, with new external indices for non-kept rows.
        /// </summary>
        /// <typeparam name="T">Type of indices used to refer to rows</typeparam>
        /// <param name="column">Column (or Table) containing rows to Garbage Collect</param>
        /// <param name="refsToColumn">Set of all columns pointing to 'column'; indices will be updated for all swapped rows</param>
        /// <param name="rowsToKeep">Booleans identifying which rows to keep</param>
        /// <param name="updater">Optional RowUpdater; if passed, mappings are added for every swapped row kept and removed row with the new index for the row.</param>
        /// <param name="rowIndexToTempIndex">Optional array providing the index in another table for each removed row; used to add mapping to RowUpdater to correctly redirect removed rows.</param>
        /// <returns></returns>
        public static bool Collect <T>(IColumn column, IEnumerable <INumberColumn <T> > refsToColumn, IReadOnlyList <bool> rowsToKeep, RowUpdater updater = null, int[] rowIndexToTempIndex = null) where T : unmanaged, IEquatable <T>
        {
            int[] replacements = null;

            int smallestToRemove = 0;
            int biggestToKeep    = column.Count - 1;
            int removeCount      = 0;

            // Walk backward and forward in rowsToKeep, swapping the earliest row to remove with the latest row to keep.
            // This logic is needed to ensure rows are never swapped more than once, so that indices to them are updated correctly.
            // This logic also ensures the minimum number of swaps are made. Swaps can be expensive for tables.
            while (smallestToRemove <= biggestToKeep)
            {
                // Walk backward, finding the first row to keep
                while (smallestToRemove <= biggestToKeep && rowsToKeep[biggestToKeep] == false)
                {
                    // While rows already at the end are being removed, tell the updater where in the temp table they've gone
                    updater?.AddMapping(biggestToKeep, rowIndexToTempIndex[biggestToKeep], movedToTemp: true);

                    biggestToKeep--;
                    removeCount++;
                }

                // Walk forward, finding the first row to be removed
                while (smallestToRemove < biggestToKeep && rowsToKeep[smallestToRemove] == true)
                {
                    smallestToRemove++;
                }

                if (smallestToRemove >= biggestToKeep)
                {
                    break;
                }
                removeCount++;

                // Swap these (the first row to remove with the last row to keep)
                column.Swap(smallestToRemove, biggestToKeep);

                // Tell the updater about the kept row (moved from biggestToKeep to smallestToRemove)
                updater?.AddMapping(biggestToKeep, smallestToRemove, movedToTemp: false);

                // Tell the updater about the removed row (originally at smallestToRemove, now in temp)
                updater?.AddMapping(smallestToRemove, rowIndexToTempIndex?[smallestToRemove] ?? -1, movedToTemp: true);

                // Set up a map array identifying the new row index for each previous row index
                if (refsToColumn != null)
                {
                    if (replacements == null)
                    {
                        replacements = new int[column.Count];

                        for (int i = 0; i < replacements.Length; ++i)
                        {
                            replacements[i] = i;
                        }
                    }

                    replacements[biggestToKeep]    = smallestToRemove;
                    replacements[smallestToRemove] = biggestToKeep;
                }

                smallestToRemove++;
                biggestToKeep--;
            }

            // Remove the rows (now swapped to the end) to be removed
            if (removeCount > 0)
            {
                column.RemoveFromEnd(removeCount);
            }

            // Update columns to refer to the updated indices
            if (refsToColumn != null && replacements != null)
            {
                IRemapper <T> remapper = RemapperFactory.Build <T>();
                refsToColumn.ForEach((column) => column.ForEach((slice) => remapper.Remap(slice, replacements)));
            }

            return(removeCount > 0);
        }