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()
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