#import "Aprica.h"
// Aprica2
// copyright Pirmin Braun 1997-2007 - pirmin@pirmin.de
// all Rights reserved;
@implementation PBDD
- (void)encodeWithCoder:(NSCoder *)coder
{ [coder encodeObject:tables];
[coder encodeObject:tableDict];
return;
}
- (id)initWithCoder:(NSCoder *)coder;
{ if(!([self init]))return nil;
[tables addObjectsFromArray:[coder decodeObject]];
[tableDict addEntriesFromDictionary:[coder decodeObject]];
isConsistent=YES;
return self;
}
- init;
{ if(!(self = [super init]))return nil;
tables = [[NSMutableArray alloc]initWithCapacity:100];
tableDict = [[NSMutableDictionary alloc]initWithCapacity:200];
isConsistent=NO;
return self;
}
- (void)dealloc
{ int i,j;
[self shrink];
for (i=0,j=[tables count];i<j;i++) { PBDDTable *t=[tables oai:i];
[[t attributes] makeObjectsPerformSelector:@selector(setMyDD:) withObject: nil];
[[t plainAttributes] makeObjectsPerformSelector:@selector(setMyDD:) withObject: nil];
[t setMyDD:nil];
}
[tableDict removeAllObjects];
[tables removeAllObjects];
[tables release];
[tableDict release];
[super dealloc];
}
- (NSArray *)loadIDMFileDict:(NSString *)path;
{ NSData *d = [NSData dataWithContentsOfFile: path];
NSString *s = [[[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding] autorelease];
NSArray *lines = [s componentsSeparatedByString: @"\n"];
NSMutableDictionary *tp;
NSMutableArray *aa;
LMDN(tpmd);
unsigned i,n,j,m;
//Build Proportylist (tpmd) from idm file.
for (i=0, n=[lines count]; i<n; i++) { NSString *val[4];
NSString *line = [lines oai: i];
NSArray *fvals = [line componentsSeparatedByString: @"\t"];
// ignore empty lines.
if ([line length]==0) continue;
// report but ignore unparsable lines.
m = [fvals count];
if (m!=4) { LOGS(([NSSWF @"corrupted line %i in idm file: %@\n'%@'",i,path,line]));
continue;
}
// setup local variables for fast access.
for (j=0; j<4; j++) { val[j] = [fvals oai: j];
}
// retrieve or create current table plist dictionary.
tp = [tpmd ofk: val[0]];
if (tp==nil) { tp = [NSMutableDictionary dictionaryWithCapacity: 5];
[tpmd setObject: tp forKey: val[0]];
[tp setObject:[NSMutableDictionary dictionary] forKey:@"_fastAttributeAccessDict"];
}
if ([val[1] isEqualToString: @"-"]) { // set table property
[tp setObject: [val[3] stringByUnescapingCRTAB] forKey: val[2]];
} else { NSMutableDictionary *ap;
NSMutableDictionary *_fastAttributeAccessDict = [tp ofk:@"_fastAttributeAccessDict"];
// set attribute property
aa = [tp ofk: @"attributes"];
if (aa==nil) { // the first object is a temporary dictionary for quick attribute access.
aa = [NSMutableArray arrayWithCapacity:10];
[tp setObject: aa forKey: @"attributes"];
}
ap = [_fastAttributeAccessDict ofk: val[1]];
if (ap==nil) { ap = [NSMutableDictionary dictionaryWithCapacity: 10];
[_fastAttributeAccessDict setObject: ap forKey: val[1]];
[aa addObject: ap];
}
[ap setObject: [val[3] stringByUnescapingCRTAB] forKey: val[2]];
}
}
return [tpmd allValues];
}
- (void)loadIDMFile:(NSString *)path;
{ NSArray *tbls;
PBDDTable *t;
unsigned i,n;
NSMutableDictionary *tp;
// remove quick attribute access dictionaries.
// PB: _fastAttributeAccessDict as tableproperty because there are tables without attributes that raised an exception
tbls = [self loadIDMFileDict:path];
for (i=0,n=[tbls count];i<n;i++) { [[tbls oai: i]removeObjectForKey:@"_fastAttributeAccessDict"];
}
// create real tables from property lists.
for(i=0,n=[tbls count];i<n;i++){ tp = [tbls oai:i];
t = [[PBDDTable alloc]init];
[t takeValuesFromDictionary:tp];
[t setMyDD:self];
[t convertDictionaries];
[self addTable:t];
[t release];
}
}
- (void)refreshFromIDMFile;
{ NSArray *tbls;
PBDDTable *t;
unsigned i,n;
NSMutableDictionary *tp;
NSString *path = [MANDANTPATH stringByAppendingPathComponent:@"Mandant.idm"];
tbls = [self loadIDMFileDict:path];
for (i=0,n=[tbls count];i<n;i++) { [[tbls oai: i]removeObjectForKey:@"_fastAttributeAccessDict"];
}
// refresh real tables from property lists.
for(i=0,n=[tbls count];i<n;i++){ tp = [tbls oai:i];
t = [self tableNamed:[tp ofk:@"dbName"]];
[t refreshAttributesFrom:[tp ofk:@"attributes"]];
}
}
- (id)initFromPath:(NSString *)mp;
{ NSString *p;
NSFileManager *fm = [NSFileManager defaultManager];
if (!(self=[self init])) return nil;
[tables removeAllObjects];
[tableDict removeAllObjects];
// load Mandant
p = [mp stringByAppendingPathComponent:@"Mandant.idm"];
if ([fm fileExistsAtPath: p]) { [self loadIDMFile: p];
} else { LOGS(([NSSWF @"'%@' nicht gefunden.", p]));
}
[self reorg];
return self;
}
- (void)reorg;
{ [tables makeObjectsPerformSelector:@selector(clean)];
[tables makeObjectsPerformSelector:@selector(getPKN)];
[tables makeObjectsPerformSelector:@selector(consolidate)];
isConsistent = YES;
}
- (void)setIsConsistent:(BOOL)yn;
{ isConsistent = yn;
}
- (BOOL)isConsistent;{return isConsistent;}- (NSMutableArray *)plainAttributes;
{ NSMutableArray *ma = [NSMutableArray arrayWithCapacity:1000];
int i,j;
PBDDTable *t;
for(i=0,j=[tables count];i<j;i++){ t = [tables oai:i];
[ma addObjectsFromArray:[t plainAttributes]];
}
return ma;
}
- (NSMutableDictionary *)tableDict;
{ return tableDict;
}
- (PBDDTable *)tableNamedCheap:(NSString *)value;
{ if(!value)return nil;
return [tableDict ofk:value];
}
- (PBDDTable *)tableNamed:(NSString *)value;
{ PBDDTable *t;
if(!value)return nil;
t = [tableDict ofk:value];
if(!t){ LOGS(([NSSWF @"Tabelle %@ nicht gefunden;",value]));
// PRINTCURRENTSTACK;
return nil;
}else{ return t;
}
}
- (void)addTables:(NSArray *)a;
{ int i,j=[a count];
for(i=0;i<j;i++){ [self addTable:[a oai:i]];
}
}
- (BOOL)addTable:pbt;
{ NSString *s = [pbt dbName];
if(!FILLED(s))return NO;
if([tableDict ofk:s])return NO;
[tables addObject:pbt];
[tableDict setSecureObject:pbt forKey:s];
[pbt setMyDD:self];
return YES;
}
- (BOOL)removeTable:pbt;
{ NSString *s = [pbt dbName];
if(!FILLED(s))return NO;
[tables removeObject:pbt];
[tableDict rofk:s];
return YES;
}
//f keyValueCoding:
- (NSArray *)tables;
{ return tables;
}
- (NSArray *)realTables;
{// convenience
return [self tablesIncludingAbstract:NO];
}
- (NSArray *)tablesIncludingAbstract:(BOOL)yn;
{ int i,j=[tables count];
PBDDTable *t;
NSMutableArray *ma;
ma = [NSMutableArray arrayWithCapacity:j];
for(i=0;i<j;i++){ t = [tables oai:i];
if(!yn && [[t type] isEqual:SC_Abstract])continue;
[ma addObject:t];
}
return ma;
}
- (NSArray *)positionsTabellenForTablenamed:(NSString *)tn;
{ //entitity, die eine relation auf die entity des eo hat ueber masterkey
LMA;
NSArray *a = [self tables];
int i,j=[a count];
for(i=0;i<j;i++){ PBDDTable *t = [a oai:i];
PBDDAttribute *pba;
if([[t type] isEqual:SC_Abstract])continue;
if((pba = [t plainAttrNamed:@"masterkey"]) && [[pba refdTableName] iE:tn]){ [lma addObjectUniq:t];
}
}
if(![lma count])return nil;
[lma sortUsingKeyOrderArray:[NSArray soaFrom:@"guiName"]];
return [lma valuesForKey:@"dbName"];
}
- (NSArray *)minimalAttributesFrom:(NSArray *)a;
{ LMA;
int i,j;
NSString *s;
for(i=0,j=[a count];i<j;i++){ s = [a oai:i];
[lma addObject:[self minimalAttributeNamed:s]];
}
return (NSArray *)lma;
}
- (PBDDAttribute *)minimalAttributeNamed:(NSString *)s;
{ PBDDAttribute *pba;
pba = [[PBDDAttribute alloc]init];
[pba setDbName:s];
[pba setLength:10];
return [pba autorelease];
}
- (void)shrink;
{//zur Laufzeit, wenn plainAttributes aufgerufen wurde, koennen die normalen Attributes der tables weggehauen werden;
//ebenso koennen die hidden tables weggehauen werden;
PBDDTable *t;
int j;
for(j=[tables count]-1;j>=0;j--){ t = [tables oai:j];
[[t attributes]removeAllObjects];
}
}
- (NSArray *)dbDifferencesToDDNew:(PBDD *)odb;
{//ein modell mit diesem vgl.;
//ist anders als Modelmerge, da nur DB-Aspekte interessieren u. nicht z.B. guiNames
//tables Existenz
//Attribute Existenz
//Attribute Typ
//Attribute Laenge bzw. scale u. precision
//Achtung! Info-String wird in BBCompareDB geparsed f. resolve-action; nicht leichtfertig aendern
PBDDTable *t,*tdb;
NSArray *ta = [self tablesIncludingAbstract:NO],*aa;
NSArray *tadb,*aadb;
NSString *s,*an;
NSDictionary *error;
static NSMutableArray *lma;
static NSMutableArray *errors;
PBDDAttribute *pba,*pbadb;
int i,j,ii,jj,dt,dtdb,pbal,pbanak;
NSAutoreleasePool *localPool=nil;
if(!lma){ lma = [[NSMutableArray alloc]initWithCapacity:100];
errors = [[NSMutableArray alloc]initWithCapacity:100];
}
[lma removeAllObjects];
[errors removeAllObjects];
// LOGS(([NSSWF @"APP.utf8db = %@",[_APP utf8db]?@"J":@"N"]));
//tables in DB aber nicht in DD
tadb = [odb tables];
for(ii=0,jj=[tadb count];ii<jj;ii++){ tdb = [tadb oai:ii];
s = [tdb dbName];
if([s hasSecurePrefix:@"query_"] || [s hasSecurePrefix:@"keyword_"])continue; // recherche-Tabellen werden dynamisch angelegt
if(!(t = [[self tableDict]objectForKey:s])){ error = [NSDictionary dictionaryWithObjectsAndKeys:
[NSSWF @"table- %@",s],@"info",
EON,@"tableName",
EON,@"attrName",
@"1",@"isPK",
nil,nil];
[errors addObject:error];
}else{ [lma addObject:t]; //gemeinsame Tables
}
}
//tables in DD aber nicht in DB
for(i=0,j=[ta count];i<j;i++){ t = [ta oai:i];
s = [t dbName];
if([s hasSecurePrefix:@"query_"] || [s hasSecurePrefix:@"keyword_"])continue; // recherche-Tabellen werden dynamisch angelegt
if(![[odb tableDict]objectForKey:s]){ error = [NSDictionary dictionaryWithObjectsAndKeys:
[NSSWF @"table+ %@",s],@"info",
s,@"tableName",
EON,@"attrName",
@"1",@"isPK",
nil,nil];
[errors addObject:error]; //im DD aber nicht in DB
}
}
for(i=0,j=[lma count];i<j;i++){ //gemeinsame localPool = [[NSAutoreleasePool alloc]init];
t = [lma oai:i];
s = [t dbName];
if([s hasSecurePrefix:@"query_"] || [s hasSecurePrefix:@"keyword_"])continue; // recherche-Tabellen werden dynamisch angelegt
tdb = [[odb tableDict]objectForKey:s];
aadb = [tdb attributes];
//attr. in DB aber nicht in DD
for(ii=0,jj=[aadb count];ii<jj;ii++){ pbadb = [aadb oai:ii];
an = [[pbadb dbName]lowercaseString];
if(!(pba = [t plainAttrNamed:an]) || ![pba isDB]){ error = [NSDictionary dictionaryWithObjectsAndKeys:
[NSSWF @"tableAttr- %@ %@",s,an],@"info",
s,@"tableName",
@"",@"attrName",
@"1",@"isPK",
nil,nil];
[errors addObject:error];
}else{//attr. mit unterschiedl. Typ
dt = [pba dataTyp];
dtdb = [pbadb dataTyp];
pbal = [pba length];
pbanak = [pba nak];
if(dt == DT_MONEY){ pbal = 11;
pbanak = 2;
dt=DT_FLOAT;
}
if(dt == DT_BOOL)dt=DT_CHAR; //char(1) gibt's nimmer; mind. 3
if(dtdb !=dt ){ error = [NSDictionary dictionaryWithObjectsAndKeys:
[NSSWF @"tableAttr_dt %@ %@ dddt=%i dbdt=%i",s,an,dt,dtdb],@"info",
s,@"tableName",
an,@"attrName",
([pba isPK]?@"0":@"1"),@"isPK",
nil,nil];
[errors addObject:error];
continue;
}
// attr. gleicher Typ aber unterschiedl. laenge
if(dt == DT_INT || dt == DT_DATE || dt == DT_DATETIME)continue; //bei integer lange nicht checken; ist in DB immer 11; in Modell fuer Anzeige evt. begrenzt
if(pbal > (255))pbal=(65535); //text length
if((dt == DT_CHAR) && ([pbadb length] != pbal)){ // / (utf8db?3:1) error = [NSDictionary dictionaryWithObjectsAndKeys:
[NSSWF @"tableAttrW %@ %@ ddW=%i dbW=%i",s,an,pbal,[pbadb length]],@"info",
s,@"tableName",
an,@"attrName",
([pba isPK]?@"0":@"1"),@"isPK",
nil,nil];
[errors addObject:error];
continue;
}
if((dt == DT_FLOAT)){ // schliesst money mit ein if(([pbadb length] != pbal)){ error = [NSDictionary dictionaryWithObjectsAndKeys:
[NSSWF @"tableAttrW %@ %@ ddW=%i dbW=%i",s,an,pbal,[pbadb length]],@"info",
s,@"tableName",
an,@"attrName",
@"1",@"isPK",
nil,nil];
[errors addObject:error];
continue;
}
if([pbadb nak] != pbanak){ error = [NSDictionary dictionaryWithObjectsAndKeys:
[NSSWF @"tableAttr_nak %@ %@ ddnak=%i dbnak=%i",s,an,pbanak,[pbadb nak]],@"info",
s,@"tableName",
an,@"attrName",
@"1",@"isPK",
nil,nil];
[errors addObject:error];
continue;
}
}
}
}
//attr. in DD aber nicht in DB
aa = [t attributesDB];
for(ii=0,jj=[aa count];ii<jj;ii++){ pba = [aa objectAtIndex:ii];
an = [[pba dbName]lowercaseString];
if(![tdb attrNamed:an]){ error = [NSDictionary dictionaryWithObjectsAndKeys:
[NSSWF @"tableAttr+ %@ %@",s,an],@"info",
s,@"tableName",
an,@"attrName",
@"1",@"isPK",
nil,nil];
[errors addObject:error];
}
}
[localPool release];
}
[errors sortUsingKeyOrderArray: [NSArray soaFrom:@"isPK,info"]];
// LOGS([errors description]);
return errors;
}
- (NSMutableString *)sqlStatmentsForChanges:(NSArray *)chgs;
{ int i,j;
NSDictionary *d;
NSArray *a;
NSString *info,*diff;
NSMutableString *sqlS = [NSMutableString stringWithCapacity:1024];
PBDDTable *t;
PBDDAttribute *pba;
NSString *k,*s;
LMD;
[sqlS setString:EON];
for(i=0,j=[chgs count];i<j;i++){ d = [chgs oai:i];
info = [d ofk:@"info"];
a = [info componentsSeparatedByString:@" "];
if([a count] < 2)continue;
diff = [a firstObject];
if([diff iE:@"table-"]){ // drop table
[sqlS appendFormat:@"drop table %@;\n",[a oai:1]];
}else if([diff iE:@"table+"]){ // create table
if(!(t = [self tableNamed:[a oai:1]]))continue;
[sqlS appendString:[_APP sqlFormatCt:t]];
}else if([diff iE:@"tableAttr-"]){ // alter table drop ...
k = [NSSWF @"alter table %@ drop ",[a oai:1]];
s = [lmd ofk:k];
if(!s){ s = [a oai:2];
}else{ s = [s stringByAppendingFormat:@", drop %@",[a oai:2]];
}
[lmd setObject:s forKey:k];
}else if([diff iE:@"tableAttr+"]){ if(!(t = [self tableNamed:[a oai:1]]))continue;
if(!(pba = [t plainAttrNamed:[a oai:2]]))continue;
k = [NSSWF @"alter table %@ add ",[a oai:1]];
s = [lmd ofk:k];
if(!s){ s = [_APP sqlFormatCANew:pba];
}else{ s = [NSSWF @"%@, add %@",s,[_APP sqlFormatCANew:pba]];
}
[lmd setObject:s forKey:k];
// to prevent NULL Values in DB
k = [NSSWF @"update %@ set ",[a oai:1]];
s = [lmd ofk:k];
if(!s){ s = [NSSWF @"%@ = '%@'",[pba dbName],[pba initValue]];
[lmd setObject:s forKey:k];
}else{ s = [s stringByAppendingFormat:@", %@ = '%@'",[pba dbName],[pba initValue]];
[lmd setObject:s forKey:k];
}
}else if([diff iE:@"tableAttrW"] || [diff iE:@"tableAttr_dt"] || [diff iE:@"tableAttr_nak"]){ // alter table modify
if(!(t = [self tableNamed:[a oai:1]]))continue;
if(!(pba = [t plainAttrNamed:[a oai:2]]))continue;
[sqlS appendFormat:@"alter table %@ modify %@;\n",[a oai:1],[_APP sqlFormatCANew:pba]];
}
}
a = [[lmd allKeys]sortedArray];
for(i=0,j=[a count];i<j;i++){ k = [a oai:i]; // alter vor update
[sqlS appendFormat:@"%@ %@;\n",k,[lmd ofk:k]];
}
return sqlS;
}
@end