Example #1
0
 def __init__(self, session, scope, output=sys.stdout):
     self.session = session
     self.scope = scope
     self.output = output
     self.valid = False
     self.db = CQDbConnection(session)
     self.tablePrefix = session.getTablePrefix()
     
     self._processEntities()
Example #2
0
class DatabaseIntegrity(object):
    def __init__(self, session, scope, output=sys.stdout):
        self.session = session
        self.scope = scope
        self.output = output
        self.valid = False
        self.db = CQDbConnection(session)
        self.tablePrefix = session.getTablePrefix()
        
        self._processEntities()
        
    def _processEntities(self):
        
        self.entities = dict()
        
        tablePrefix = self.session.getTablePrefix()
        select = 'SELECT %(select)s FROM %(from)s WHERE %(where)s'
        update = 'UPDATE %(from)s SET %(update)s WHERE %(where)s'
        wheres = [
            '%(fieldDbName)s IS NULL',
            '%(fieldDbName)s NOT IN ' \
            '(SELECT DISTINCT dbid FROM %(tablePrefix)s.%(refEntityDefDbName)s)'
        ]
        
        valid = True
        
        for entityDef in self.session.getAllEntityDefs():
            entityDefName = entityDef.GetName()
            
            if entityDefName in ('history', 'ratl_replicas', 'attachments'):
                continue
            
            entityDefDbName = entityDef.GetDbName()
            entityDefTable = '%s.%s' % (self.tablePrefix, entityDefDbName)
            uniqueKey = entityDef.getUniqueKey()
            ourInfo = uniqueKey._info()
            
            for fieldName in entityDef.GetFieldDefNames():
                fieldType = entityDef.GetFieldDefType(fieldName)
                fieldDbName = entityDef.getFieldDbName(fieldName)
                
                if fieldType != FieldType.Reference:
                    continue
                
                refEntityDef = entityDef.GetFieldReferenceEntityDef(fieldName)
                refEntityDefDbName = refEntityDef.GetDbName() 
                refUniqueKey = refEntityDef.getUniqueKey()
                refInfo = refUniqueKey._info()
                
                nullSql = select % {
                    'select': 'COUNT(*)',
                    'from'  : entityDefTable,
                    'where' : wheres[0] % locals()
                }
                invalidSql = select % {
                    'select': 'COUNT(*)',
                    'from'  : entityDefTable,
                    'where' : wheres[1] % locals()
                }
                nullCount = self.db.selectSingle(nullSql)
                invalidCount = self.db.selectSingle(invalidSql)
                
                assert nullCount >= 0 and invalidCount >= 0
                
                if nullCount > 0 or invalidCount > 0:
                    results = {
                        'null'    : nullCount,
                        'invalid' : invalidCount,
                    }
                    # Note that we delay entering anything into the entities map
                    # until we reach this point, when we can be certain we've
                    # found a discrepancy.
                    self.entities.setdefault(entityDefName, dict()) \
                                 .setdefault(fieldName, dict())     \
                                 .update(results)
                
                    if self.scope == IntegrityScope.Fix:
                        where = [ w % locals() for w in wheres ]
                        k = {
                            'from'   : entityDefTable,
                            'update' : '%s = 0' % fieldDbName,
                            'where'  : ' OR '.join(where)
                        }
                        sql = update % k
                        self.db.execute(sql)
                    else:
                        valid = False
                        
        self.valid = valid
        if self.entities:
            
            fixed = 'Yes' if self.scope == IntegrityScope.Fix else 'No'
            
            header = ('Entity', 'Field', 'Null', 'Invalid', 'Fixed?')
            cols = len(header)
            rows = [ 
                header,
                ('',) * cols,
            ]
            
            for (entity, fields) in self.entities.items():
                count = itertools.count(0)
                for (field, r) in fields.items():
                    # Only print out the entity name for the first row.
                    name = '' if count.next() else entity
                    rows.append((name, field, r['null'], r['invalid'], fixed))
                    
            header = (
                '[%s Database]' % self.session._databaseName,
                'Entity Integrity Report'
            )
            renderTextTable(header, rows, output=self.output)
            
    def _processHistory(self):
        pass
    
    def _processUsersAndGroups(self):
        pass
    
    def _processParentChildLinks(self):
        pass