/// /// Copyright © 2003-2008 JetBrains s.r.o. /// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder). /// using System; using System.Collections; using System.Diagnostics; using System.IO; using JetBrains.DataStructures; using JetBrains.Omea.Base; using JetBrains.Omea.Database; using JetBrains.Omea.OpenAPI; namespace JetBrains.Omea.ResourceStore { /// /// The class which performs automatic diagnostics and repair of the resource store. /// public class ResourceStoreRepair { private IDatabase _db; private ITable _propTypes; private ITable _resTypes; private ITable _intProps; private ITable _stringProps; private ITable _longStringProps; private ITable _dateProps; private ITable _doubleProps; private ITable _blobProps; private ITable _boolProps; private ITable _resources; private ITable _links; private ITable _stringListProps; private int _errorCount = 0; private int _fixCount = 0; private int _resCount = 0, _propCount = 0, _linkCount = 0; private bool _fixErrors; private bool _dumpStructure; private IntHashTable _propTypeMap = new IntHashTable(); // property type ID -> name private HashSet _propTypeNames = new HashSet(); private IntHashTable _propDataTypes = new IntHashTable(); private HashSet _resTypeIDs = new HashSet(); private Hashtable _resTypeNames = new Hashtable(); private Exception _repairException = null; public event RepairProgressEventHandler RepairProgress; public ResourceStoreRepair( IDatabase db ) { _db = db; } private void ShowProgress( string message, params object[] args ) { string result = message; if ( args.Length > 0 ) { result = String.Format( message, args ); } if ( RepairProgress != null ) { RepairProgress( this, new RepairProgressEventArgs( result ) ); } } private void ReportError( string msg ) { if ( _errorCount > 100 && !_fixErrors ) return; ShowProgress( msg ); if ( _errorCount == 100 && !_fixErrors ) { Console.WriteLine( "Too many errors found, stopping further report" ); } _errorCount++; } public bool FixErrors { get { return _fixErrors; } set { _fixErrors = value; } } public bool DumpStructure { get { return _dumpStructure; } set { _dumpStructure = value; } } public Exception RepairException { get { return _repairException; } } public void Run() { _propTypes = _db.GetTable( "PropTypes" ); _resTypes = _db.GetTable( "ResourceTypes" ); _intProps = _db.GetTable( "IntProps" ); _stringProps = _db.GetTable( "StringProps" ); _longStringProps = _db.GetTable( "LongStringProps" ); _stringListProps = _db.GetTable( "StringListProps" ); _dateProps = _db.GetTable( "DateProps" ); _blobProps = _db.GetTable( "BlobProps" ); _doubleProps = _db.GetTable( "DoubleProps" ); _boolProps = _db.GetTable( "BoolProps" ); _resources = _db.GetTable( "Resources" ); _links = _db.GetTable( "Links" ); try { ShowProgress( "Processing property types..." ); RepairPropTypes(); ShowProgress( "Processing resource types..." ); RepairResourceTypes(); ShowProgress( "Processing resources..." ); RepairResources(); ShowProgress( "Processing IntProps..." ); RepairProps( _intProps, PropDataType.Int ); ShowProgress( "Processing StringProps..." ); RepairProps( _stringProps, PropDataType.String ); ShowProgress( "Processing LongStringProps..." ); RepairProps( _longStringProps, PropDataType.LongString ); ShowProgress( "Processing DateProps..." ); RepairProps( _dateProps, PropDataType.Date ); ShowProgress( "Processing BlobProps..." ); RepairProps( _blobProps, PropDataType.Blob ); ShowProgress( "Processing DoubleProps..." ); RepairProps( _doubleProps, PropDataType.Double ); ShowProgress( "Processing StringListProps..." ); RepairProps( _stringListProps, PropDataType.StringList ); ShowProgress( "Processing BoolProps..." ); RepairProps( _boolProps, PropDataType.Bool ); ShowProgress( "Processing links..." ); RepairLinks(); if (_fixErrors) { ShowProgress("Repairing BlobFileSystem..."); _db.RepairBlobFileSystem(); } } catch( Exception e ) { ShowProgress( "Fatal error during repair: " + e.Message ); _repairException = e; _errorCount++; } ShowProgress( "Closing database..." ); _db.Shutdown(); if ( _errorCount <= _fixCount ) { MyPalStorage.OpenDatabase(); try { RepairRestrictions(); MyPalStorage.CloseDatabase(); } catch( StorageException e ) { ShowProgress( e.ToString() ); } finally { ShowProgress( "done." ); } } else { ShowProgress( "Link restrictions were not checked because errors were found on earlier stages" ); } ShowProgress( "Processed {0} resources, {1} properties and {2} links", _resCount, _propCount, _linkCount ); if ( _errorCount == 0 ) { ShowProgress( "No errors found" ); } else { ShowProgress( "{0} errors found, {1} errors fixed" , _errorCount, _fixCount ); } } private void RepairPropTypes() { int propID = GetPropId( "ID" ); int propName = GetPropId( "Name" ); int propDataType = GetPropId( "DataType" ); int propFlags = GetPropId( "Flags" ); int typePropType = GetResourceTypeId( "PropType" ); bool needRebuildIndexes = false; if ( _propTypes.PeekNextID() > 65536 ) { needRebuildIndexes = true; } using( IResultSet rs = _propTypes.CreateResultSet( 0 ) ) { foreach( IRecord rec in rs ) { int typeId = rec.GetIntValue( 0 ); string name = rec.GetStringValue( 1 ); int dataType = rec.GetIntValue( 2 ); int flags = rec.GetIntValue( 3 ); if ( typeId < 0 || typeId > 65536 ) { ReportError( "Invalid property type ID " + typeId ); if ( _fixErrors ) { rec.Delete(); _fixCount++; needRebuildIndexes = true; continue; } } if ( _propTypeMap.Contains( typeId ) ) { ReportError( "Duplicate property ID " + typeId ); } if ( _propTypeNames.Contains( name ) ) { ReportError( "Duplicate property name " + name ); } _propTypeMap.Add( typeId, name ); _propTypeNames.Add( name ); _propDataTypes [typeId] = dataType; if ( _dumpStructure ) { ShowProgress( "Property type {0}, name {1}, dataType {2}, flags {3}", typeId, name, dataType, (PropTypeFlags) flags ); } int resID; try { resID = FindResource( typePropType, _stringProps, propName, name ); } catch( Exception e ) { ReportError( "Property " + name + ": " + e.Message ); continue; } if ( resID == -1 ) { ReportError( "Could not find matching resource for property name " + name ); if ( _fixErrors ) { try { int resourceId = CreateResource( typePropType ); CreatePropValue( _stringProps, resourceId, propName, name ); CreatePropValue( _intProps, resourceId, propID, typeId ); CreatePropValue( _intProps, resourceId, propDataType, dataType ); CreatePropValue( _intProps, resourceId, propFlags, flags ); _fixCount++; } catch( Exception e ) { ReportError( "Failed to create property type resource: " + e.Message ); } } continue; } object idValue = GetPropValueSafe( _intProps, resID, propID ); if ( idValue == null ) { ReportError( "ID property not found for property " + name ); } else if ( (int) idValue != typeId ) { ReportError( "ID property for property " + name + " does not match ID value" ); if ( _fixErrors ) { Trace.WriteLine( "Set ID property of PropType resource " + name + " to " + typeId ); UpdatePropValue( _intProps, resID, propID, typeId ); _fixCount++; } } object dataTypeValue = GetPropValueSafe( _intProps, resID, propDataType ); if ( dataTypeValue == null ) { ReportError( "DataType property not found for property " + name ); } else if ( (int) dataTypeValue != dataType ) { ReportError( "DataType property for property " + name + " does not match Type value" ); } object flagsValue = GetPropValueSafe( _intProps, resID, propFlags ); if ( flagsValue == null ) { ReportError( "Flags property not found for property " + name ); } else if ( (int) flagsValue != flags ) { ReportError( "Flags property for property " + name + " does not match Flags value. Fixing..." ); UpdatePropValue( _intProps, resID, propFlags, flags ); _fixCount++; } } } if ( needRebuildIndexes ) { _propTypes.RebuildIndexes( true ); } } private void RepairResourceTypes() { int propID = GetPropId( "ID" ); int propName = GetPropId( "Name" ); int propDNMask = GetPropId( "DisplayNameMask" ); int typeResType = GetResourceTypeId( "ResourceType" ); bool needRebuildIndexes = false; if ( _resTypes.PeekNextID() > 65536 ) { needRebuildIndexes = true; } using( IResultSet rs = _resTypes.CreateResultSet( 0 ) ) { foreach( IRecord rec in rs ) { int typeId = rec.GetIntValue( 0 ); string name = rec.GetStringValue( 1 ); string displayNameMask = rec.GetStringValue( 2 ); if ( _dumpStructure ) { ShowProgress( "Resource type {0}, name {1}, displayNameMask {2}", typeId, name, displayNameMask ); } if ( typeId < 0 || typeId > 65536 ) { ReportError( "Invalid resource type ID " + typeId ); if ( _fixErrors ) { rec.Delete(); _fixCount++; needRebuildIndexes = true; continue; } } if ( _resTypeIDs.Contains( typeId ) ) { ReportError( "Duplicate resource type ID " + typeId ); if ( _fixErrors ) { rec.Delete(); _fixCount++; needRebuildIndexes = true; } continue; } if ( _resTypeNames.ContainsValue( name ) ) { ReportError( "Duplicate resource type name " + name ); if ( _fixErrors ) { rec.Delete(); _fixCount++; needRebuildIndexes = true; } continue; } _resTypeIDs.Add( typeId ); _resTypeNames.Add( typeId, name ); int resID; try { resID = FindResource( typeResType, _stringProps, propName, name ); } catch( Exception e ) { ReportError( "Resource type " + name + ": " + e.Message ); continue; } if ( resID == -1 ) { ReportError( "Could not find matching resource for resource type name " + name ); if ( _fixErrors ) { try { int resourceId = CreateResource( typeResType ); CreatePropValue( _stringProps, resourceId, propName, name ); CreatePropValue( _intProps, resourceId, propID, typeId ); CreatePropValue( _stringProps, resourceId, propDNMask, displayNameMask ); // we don't restore the flags, but they'll be reregistered on next run of Omea _fixCount++; } catch( Exception e ) { ReportError( "Failed to create resource type resource: " + e.Message ); } } continue; } object idValue = GetPropValueSafe( _intProps, resID, propID ); if ( idValue == null ) { ReportError( "ID property not found for resource type " + name ); } else if ( (int) idValue != typeId ) { ReportError( "ID property for resource type " + name + " does not match ID value" ); } object dnMaskValue = GetPropValueSafe( _stringProps, resID, propDNMask ); if ( dnMaskValue == null ) { ReportError( "DisplayNameMask property not found for resource type " + name ); if ( _fixErrors ) { CreatePropValue( _stringProps, resID, propDNMask, displayNameMask ); _fixCount++; } } else if ( (string) dnMaskValue != displayNameMask ) { ReportError( "DisplayNameMask property for resource type " + name + " does not match DisplayNameMask value. Fixing..." ); if ( _fixErrors ) { UpdatePropValue( _stringProps, resID, propDNMask, displayNameMask ); _fixCount++; } } } } if ( needRebuildIndexes ) { _resTypes.RebuildIndexes( true ); } } private void RepairResources() { int maxResID = -1; HashSet resIDs = new HashSet(); using( IResultSet rs = _resources.CreateResultSet( 0 ) ) { foreach( IRecord rec in rs ) { _resCount++; int resID = rec.GetIntValue( 0 ); int typeID = rec.GetIntValue( 1 ); if ( resID > maxResID ) { maxResID = resID; } if ( _dumpStructure ) { string typeName = (string) _resTypeNames [typeID]; if ( typeName == null ) typeName = Convert.ToString( typeID ); ShowProgress( "Resource " + resID + " of type " + typeName ); } if ( !_resTypeIDs.Contains( typeID ) ) { ReportError( "Found a resource of a non-existing type " + typeID ); if ( _fixErrors ) { rec.Delete(); _fixCount++; } continue; } if ( resIDs.Contains( resID ) ) { ReportError( "Duplicate resource ID " + resID ); if ( _fixErrors ) { rec.Delete(); _fixCount++; } continue; } resIDs.Add( resID ); } } int nextID = _resources.NextID(); if ( nextID <= maxResID ) { ReportError( "Next ID for table Resources " + nextID + " is smaller than maximum resource ID " + maxResID ); } } private void RepairProps( ITable propTable, PropDataType dataType ) { HashSet resPropTypes = new HashSet(); int lastResID = -1; using( IResultSet rs = propTable.CreateResultSet( 0 ) ) { IEnumerator enumerator = rs.GetEnumerator(); try { while( enumerator.MoveNext() ) { IRecord rec; try { rec = (IRecord) enumerator.Current; } catch( AttemptReadingDeletedRecordException ) { ReportError( "Deleted record found in index for " + dataType + " property table" ); continue; } _propCount++; int resID = rec.GetIntValue( 0 ); int propType = rec.GetIntValue( 1 ); if ( resID != lastResID ) { lastResID = resID; resPropTypes.Clear(); } IRecord resRec = _resources.GetRecordByEqual( 0, resID ); if ( resRec == null ) { ReportError( "Found a property of a non-existing resource " + resID ); if ( _fixErrors ) { rec.Delete(); _fixCount++; } continue; } if ( !_propTypeMap.Contains( propType ) ) { ReportError( "Found a property with an invalid type " + propType ); if ( _fixErrors ) { rec.Delete(); _fixCount++; } continue; } if ( (int) _propDataTypes [propType] != (int) dataType ) { ReportError( "Type of property " + propType + " does not match type of table " + dataType ); if ( _fixErrors ) { rec.Delete(); _fixCount++; } continue; } string propTypeName = (string) _propTypeMap [propType]; if ( dataType != PropDataType.StringList && resPropTypes.Contains( propType ) ) { ReportError( "Duplicate property " + propTypeName + " of resource " + resID ); if ( _fixErrors ) { rec.Delete(); _fixCount++; } continue; } if (dataType == PropDataType.Blob) { IBLOB blob = rec.GetBLOBValue(2); Stream stream; try { stream = blob.Stream; } catch (IOException) { ReportError("Missing blob stream for property " + propTypeName + " of resource " + resID); if (_fixErrors) { rec.Delete(); _fixCount++; } continue; } try { long length = stream.Length; byte[] buffer = new byte[4096]; for (long bytesRead = 0; bytesRead < length; bytesRead += 4096) { int bytesToRead = Math.Min(4096, (int)(length - bytesRead)); stream.Read(buffer, 0, bytesToRead); } } catch (IOException) { ReportError("Failed to read blob stream for property " + propTypeName + " of resource " + resID); if (_fixErrors) { rec.Delete(); _fixCount++; } } stream.Close(); } else if (dataType == PropDataType.String || dataType == PropDataType.LongString) { try { rec.GetStringValue(2); } catch (IOException ex) { ReportError("Failed to read string value for property " + propTypeName + " of resource " + resID); if (_fixErrors) { rec.Delete(); _fixCount++; } } } resPropTypes.Add( propType ); } } finally { IDisposable disp = enumerator as IDisposable; if ( disp != null ) { disp.Dispose(); } } } } private class LinkData { int _id1; int _id2; int _linkType; internal LinkData( int id1, int id2, int linkType ) { _id1 = id1; _id2 = id2; _linkType = linkType; } public override bool Equals( object obj ) { if ( !(obj is LinkData) ) return false; LinkData other = (LinkData) obj; return _id1 == other._id1 && _id2 == other._id2 && _linkType == other._linkType; } public override int GetHashCode() { return _id1 ^ _id2 ^ (_linkType << 16); } } private void RepairLinks() { HashSet _existingLinks = new HashSet(); using( IResultSet rs = _links.CreateResultSet( 0 ) ) { foreach( IRecord rec in rs ) { _linkCount++; int id1 = rec.GetIntValue( 0 ); int id2 = rec.GetIntValue( 1 ); int propType = rec.GetIntValue( 2 ); if ( !_propTypeMap.Contains( propType ) ) { ReportError( "Found a link with an invalid type " + propType ); if ( _fixErrors ) { rec.Delete(); _fixCount++; } continue; } string propTypeName = (string) _propTypeMap [propType]; if ( id1 == id2 ) { ReportError( "Found a link of type " + propTypeName + " of resource " + id1 + " to itself" ); if ( _fixErrors ) { rec.Delete(); _fixCount++; } continue; } if ( _existingLinks.Contains( new LinkData( id2, id1, propType ) ) ) { ReportError( "Found a recursive link of type " + propTypeName + " between " + id1 + " and " + id2 ); if ( _fixErrors ) { rec.Delete(); _fixCount++; } continue; } LinkData linkData = new LinkData( id1, id2, propType ); if ( _existingLinks.Contains( linkData ) ) { ReportError( "Found a duplicate link of type " + propTypeName + " between " + id1 + " and " + id2 ); if ( _fixErrors ) { rec.Delete(); _fixCount++; } continue; } _existingLinks.Add( linkData ); IRecord resRec = _resources.GetRecordByEqual( 0, id1 ); if ( resRec == null ) { ReportError( "Found a link of type " + propTypeName + " of a non-existing resource " + id1 ); if ( _fixErrors ) { rec.Delete(); _fixCount++; } continue; } resRec = _resources.GetRecordByEqual( 0, id2 ); if ( resRec == null ) { ReportError( "Found a link of type " + propTypeName + " of a non-existing resource " + id2 ); if ( _fixErrors ) { rec.Delete(); _fixCount++; } continue; } if ( (int) _propDataTypes [propType] != (int) PropDataType.Link ) { ReportError( "Non-link property " + propTypeName + " found in Links table for resources " + id1 + " and " + id2 ); if ( _fixErrors ) { rec.Delete(); _fixCount++; } continue; } } } } public void RepairRestrictions() { ShowProgress( "Processing link restrictions" ); RepairLinkRestrictions(); ShowProgress( "Processing unique property value restrictions" ); RepairUniqueRestrictions(); } private void RepairLinkRestrictions() { IResourceStore store = MyPalStorage.Storage; IResourceList restrictionsList = store.GetAllResources( "LinkRestriction" ); HashSet involvedResTypes = new HashSet(); PropTypeCollection propTypes = (PropTypeCollection) MyPalStorage.Storage.PropTypes; foreach( IResource lr in restrictionsList ) { int linkType = lr.GetIntProp( "LinkType" ); if ( !propTypes.IsValidType( linkType ) ) { lr.Delete(); } else { string fromResourceType = lr.GetStringProp( "fromResourceType" ); if ( fromResourceType != null ) { involvedResTypes.Add( fromResourceType ); } } } restrictionsList = store.GetAllResources( "LinkRestriction" ); foreach( HashSet.Entry E in involvedResTypes ) { string resType = (string) E.Key; ShowProgress( "Checking link restrictions for resources of type '{0}'...", resType ); IResourceList resources = store.GetAllResources( resType ); foreach( IResource resource in resources ) { foreach( IResource lr in restrictionsList ) { if( lr.GetStringProp( "fromResourceType" ) == resource.Type ) { RepairLinkRestriction( lr, resource ); } } } } } private void RepairLinkRestriction( IResource lr, IResource resource ) { IResourceStore store = MyPalStorage.Storage; string toResourceType = lr.GetStringProp( "toResourceType" ); string resName = !String.IsNullOrEmpty( resource.DisplayName ) ? resource.DisplayName : ""; int linkType = lr.GetIntProp( "LinkType" ); int minCount = lr.GetIntProp( "MinCount" ); int maxCount = lr.GetIntProp( "MaxCount" ); string linkTypeName = (string) _propTypeMap [linkType]; IResourceList links; int linkTypeReverse; if ( store.PropTypes [linkType].HasFlag( PropTypeFlags.DirectedLink) ) { links = resource.GetLinksFrom( null, linkType ); linkTypeReverse = -linkType; } else { links = resource.GetLinksOfType( null, linkType ); linkTypeReverse = linkType; } /** * check destination resource types */ if( toResourceType != null ) { foreach( IResource link in links ) { if( link.Type != toResourceType ) { ReportError( "Restricted link found: resource ID=" + resource.Id + " [" + resName + "] link [" + linkTypeName + "] destination resource type=[" + link.Type + "]" ); if( _fixErrors ) { if ( store.GetMinLinkCountRestriction( resource.Type, linkType ) > resource.GetLinkCount( linkType ) && store.GetMinLinkCountRestriction( link.Type, linkTypeReverse ) > link.GetLinkCount( linkTypeReverse ) ) { resource.DeleteLink( linkType, link ); ShowProgress( "Link deleted" ); ++_fixCount; } else { ShowProgress( "Can not delete link due to min/max restrictions: {0}/{1} in store vs {2}/{3} in resource", store.GetMinLinkCountRestriction( resource.Type, linkType ).ToString(), store.GetMinLinkCountRestriction( link.Type, linkTypeReverse ).ToString(), resource.GetLinkCount( linkType ).ToString(), link.GetLinkCount( linkTypeReverse ).ToString() ); // NB: rough hack. To be deleted after the problem is identified // in the source code. if( resource.Type == "ContactName" ) { resource.Delete(); } } } } } } /** * check counts */ if( links.Count < minCount ) { ReportError( String.Format( "Not enough links of type {3} for resource '{2}' {0} minimum, {1} found", minCount, links.Count, resource, linkTypeName ) ); if( _fixErrors && IsSafeToDeleteResource( resource ) ) { resource.Delete(); ++_fixCount; } } if( maxCount >= 0 && links.Count > maxCount ) { ReportError( String.Format( "Too many links of type {3} for resource '{2}': {0} maximum, {1} found", maxCount, links.Count, resource, linkTypeName ) ); if( _fixErrors ) { int linkCount = links.Count; resource.BeginUpdate(); for( int i = links.Count-1; i >= 0 && linkCount > maxCount; i-- ) { IResource target = links [i]; // make sure we don't corrupt the DB by deleting the link if ( target.GetLinksOfType( null, linkTypeReverse ).Count > store.GetMinLinkCountRestriction( target.Type, linkTypeReverse ) ) { ShowProgress( "Deleting link to resource " + links [i] ); resource.DeleteLink( linkType, links[ i ] ); linkCount--; } } resource.EndUpdate(); ++_fixCount; } } } /** * Checks if deleting the specified resource will break minimum link count * restrictions on linked resources. */ private bool IsSafeToDeleteResource( IResource res ) { foreach( int linkType in res.GetLinkTypeIds() ) { IResourceList links = res.GetLinksOfType( null, linkType ); for( int i=0; i 0 && target.GetLinkCount( linkType ) == minCount ) { return false; } } } return true; } private void RepairUniqueRestrictions() { IResourceStore store = MyPalStorage.Storage; IResourceList restrictionsList = store.GetAllResources( "UniqueRestriction" ); HashSet involvedResTypes = new HashSet(); PropTypeCollection propTypes = (PropTypeCollection) MyPalStorage.Storage.PropTypes; foreach( IResource lr in restrictionsList ) { int uniquePropId = lr.GetIntProp( "UniquePropId" ); if ( !propTypes.IsValidType( uniquePropId ) ) { lr.Delete(); } else { string fromResourceType = lr.GetStringProp( "fromResourceType" ); if ( fromResourceType != null ) { involvedResTypes.Add( fromResourceType ); } } } restrictionsList = store.GetAllResources( "UniqueRestriction" ); foreach( HashSet.Entry E in involvedResTypes ) { string resType = (string) E.Key; ShowProgress( "Checking unique restrictions for resources of type '{0}'...", resType ); IResourceList resources = store.GetAllResources( resType ); HashMap propValues = new HashMap(); foreach( IResource resource in resources ) { IntHashSet propIds = new IntHashSet(); foreach( IResource ur in restrictionsList ) { if( ur.GetStringProp( "fromResourceType" ) == resType ) { int propId = ur.GetIntProp( "UniquePropId" ); if ( propIds.Contains( propId ) ) // do not process duplicate restrictions { continue; } propIds.Add( propId ); object propValue = resource.GetProp( propId ); if ( propValue != null ) { HashSet propValueSet = (HashSet) propValues [propId]; if ( propValueSet == null ) { propValueSet = new HashSet(); propValues [propId] = propValueSet; } if ( propValueSet.Contains( propValue ) ) { ReportError( "Unique property value restriction violated: resource ID=" + resource.Id + " property " + store.PropTypes [propId].Name + ", value " + propValue ); if( _fixErrors && IsSafeToDeleteResource( resource ) ) { resource.Delete(); ++_fixCount; } } propValueSet.Add( propValue ); } } } } } } private int FindResource( int typeID, ITable propTable, int propID, object propValue ) { int foundID = -1; using( IResultSet rs = propTable.CreateResultSet( 1, propID, 2, propValue, false ) ) { foreach( IRecord rec in rs ) { int id = rec.GetIntValue( 0 ); IRecord resRec = _resources.GetRecordByEqual( 0, id ); if ( resRec != null ) { if ( resRec.GetIntValue( 1 ) == typeID ) { foundID = id; break; } } } } return foundID; } private int GetPropId( string name ) { IRecord recID = _propTypes.GetRecordByEqual( 1, name ); if ( recID == null ) { throw new Exception( "Fatal error: '" + name + "' property not found" ); } return recID.GetIntValue( 0 ); } private int GetResourceTypeId( string name ) { IRecord recPropType = _resTypes.GetRecordByEqual( 1, name ); if ( recPropType == null ) { throw new Exception( "Fatal error: '" + name + "' resource type not found" ); } return recPropType.GetIntValue( 0 ); } private object GetPropValueSafe( ITable propTable, int resId, int propId ) { using( IResultSet rs = propTable.CreateResultSet( 0, resId, 1, propId, true ) ) { foreach( IRecord rec in rs ) { return rec.GetValue( 2 ); } return null; } } private void UpdatePropValue( ITable propTable, int resID, int propID, object value ) { using( IResultSet rs = propTable.CreateResultSet( 0, resID, 1, propID, false ) ) { bool hasDuplicates = false; foreach( IRecord rec in rs ) { if( hasDuplicates ) { rec.Delete(); } else { hasDuplicates = true; rec.SetValue( 2, value ); rec.Commit(); } } } } private void CreatePropValue( ITable propTable, int resID, int propID, object value ) { IRecord rec = propTable.NewRecord(); rec.SetValue( 0, IntInternalizer.Intern( resID ) ); rec.SetValue( 1, IntInternalizer.Intern( propID ) ); rec.SetValue( 2, value ); rec.Commit(); } private int CreateResource( int resourceTypeId ) { IRecord rec = _resources.NewRecord(); rec.SetValue( 1, resourceTypeId ); rec.Commit(); return rec.GetID(); } } public class RepairProgressEventArgs: EventArgs { private string _message; public RepairProgressEventArgs( string message ) { _message = message; } public string Message { get { return _message; } } } public delegate void RepairProgressEventHandler( object sender, RepairProgressEventArgs e ); }