#import "IntarS.h" // IntarS // copyright Pirmin Braun 1997-2006 - pirmin@pirmin.de // all Rights reserved; @implementation PBMySQLChannel ACCESSClassm(selectedAttributes,setSelectedAttributes,NSArray) - init; { if(!(self = [super init]))return nil; sock = NULL; isOpen = NO; isFetchInProgress = NO; dbNr = 0; return self; } - (int)dbNr; { return dbNr; } - (BOOL)isOpen; { return isOpen; } - (BOOL)isFetchInProgress; { return isFetchInProgress && isOpen; } - (BOOL)openChannel; { isOpen = [self connect]; isUTF8 = NO; if(isOpen){ NSDictionary *d; [self evaluateSQL:@"show variables like 'character_set_database%';"]; if(![self isFetchInProgress]){ LOGS((@"show variables failed")); return YES; //egal, kann ja auch alte MySQL sein } [self setSelectedAttributes:[self describeResults]]; d = [self fetchRow]; [self cancelFetch]; if(!d){ LOGS((@"show variables returned no selectedAttributes")); return YES; } if([[d ofk:@"Value"] iE:@"utf8"]){ isUTF8 = YES; [self evaluateSQL:@"set names utf8;"]; LOGS(@"detected UTF8 encoding, setting names to utf8"); }else{ LOGS(@"using default encoding Latin1"); } } return isOpen; } - (void)dealloc; { [selectedAttributes release]; [super dealloc]; } - (void)closeChannel; { if (sock >= 0) { if ([self isFetchInProgress]) { [self cancelFetch]; } pb_mysql_close(sock); sock = NULL; isOpen = NO; } } - (BOOL)connect; { NSString *hostname; NSString *database; NSString *user; NSString *password; NSString *hostnameName,*userName,*passwordName; dbNr = [_APP dbNr]; if(dbNr){ hostnameName = [NSSWF @"hostname%i",dbNr]; database = [[_APP configDict]ofk:[NSSWF @"database%i",dbNr]]; userName = [NSSWF @"dbuser%i",dbNr]; passwordName = [NSSWF @"dbpw%i",dbNr]; }else{ hostnameName = @"hostname"; database = [NSSWF @"%@_%@",[NSAPPNAME lowercaseString],[_APP db_mandant]]; userName = @"dbuser"; passwordName = @"dbpw"; } hostname = [[_APP configDict]ofk:hostnameName]; user = [[_APP configDict]ofk:userName]; password = [[_APP configDict]ofk:passwordName]; if(!FILLED(hostname))hostname = @"localhost"; if(!FILLED(user))user = @"root"; if(!FILLED(password))password = @"root"; sock = pb_mysql_init(sock); sock = pb_mysql_connect(sock, (char *)[hostname cString], (char *)[user cString], (char *)[password cString]); if (!sock) { LOGS(([NSSWF @"keine Verbindung mit diesen Einstellungen: hostname %@ database %@ user %@ password %@",hostname,database,user,password])); return NO; } if (pb_mysql_select_db(sock, (char *)[database cString]) < 0) { NSString *error = [NSString stringWithFormat:@"Could not open MySQL database %@", database]; LOGS(error); pb_mysql_close(sock); return NO; } return YES; } - (unsigned)insertRow:(NSDictionary *)row forTable:(PBDDTable *)t; { NSMutableString *ms; unsigned n; if(!row || !t)return 0; if(!(ms = [_APP sqlValuesStringFromDict:row forTable:t]))return 0; [ms insertString:[NSSWF @"insert %@ set ",[t dbName]] atIndex:0]; [ms appendString:@";"]; n = [self evaluateSQL:ms]; //insert returned keinen resultset und muss daher auch nicht beenden [self endFetch]; return n; } - (unsigned)updateRow:(NSDictionary *)row forTable:(PBDDTable *)t pk:(NSString *)pk; { //mit dem primary key updaten; wird hier extra uebergeben, da er in den delta-werten der row nicht drin ist NSString *pkn = [t primaryKeyName]; NSMutableString *ms; unsigned n; if(!row || !t || !FILLED(pk))return 0; if(!(ms = [_APP sqlValuesStringFromDict:row forTable:t]))return 0; [ms insertString:[NSSWF @"update low_priority %@ set ",[t dbName]] atIndex:0]; [ms appendString:[NSSWF @" where %@=\"%@\";",pkn,pk]]; n = [self evaluateSQL:ms]; //update returned keinen resultset und muss daher auch nicht beenden [self endFetch]; return n; } - (unsigned)deleteRow:(NSDictionary *)dict forTable:(PBDDTable *)t; { //mit dem primary key deleten; NSString *pkn = [t primaryKeyName],*s; unsigned n; if(!dict || !t)return 0; s = [NSSWF @"delete from %@ where %@=\"%@\";",[t dbName],pkn,[[dict ofk:pkn] mysqlEscapedString]]; n = [self evaluateSQL:s]; //delete returned keinen resultset und muss daher auch nicht beenden [self endFetch]; return n; } - (unsigned)selectAttributes:(NSArray *)attributes qualifier:(EOQualifier *)q forTable:(PBDDTable *)t offset:(int)offset count:(int)count soa:(NSArray *)soa; { return [self selectAttributes:attributes qualifier:q forTable:t tn:[t dbName] offset:offset count:count soa:soa]; } - (unsigned)selectAttributes:(NSArray *)attributes qualifier:(EOQualifier *)q forTable:(PBDDTable *)t tn:(NSString *)tn offset:(int)offset count:(int)count soa:(NSArray *)soa; { //t ist fuer die Attribute //von tn wird gelesen //bei seq. access ist tn eine vorher erstellte temptable mit den abgegrenzten daten; //diese wird dann satzweise verarbeitet; die echte table steht dann schon wieder zur verfuegung, waehrend die tn bis zum ende der verarbeitung gesperrt ist NSString *wc,*s,*colNames,*obyc,*limitc=EON; BOOL useResult; //use or store Result; wenn tn != [t dbName] -> seq.Access -> useResult if(!t)return 0; if(![attributes count]){ attributes = ATisDB(t); //wg. reihenfolge nicht * } [self setSelectedAttributes:attributes]; colNames = [[attributes valuesForKey:@"dbName"]componentsJoinedByString:@","]; wc = [_APP whereClauseFrom:q forTable:t]; obyc = [_APP orderbyClauseFrom:soa forTable:t]; if(FILLED(wc))wc = [NSSWF @" where %@",wc]; if(count)limitc = [NSSWF @"limit %i,%i",offset,count]; s = [NSSWF @"select %@ from %@ %@ %@ %@;",colNames,tn,wc,obyc,limitc]; #warning temporary workaround!!! useResult = NO; // ![tn iE:[t dbName]]; // sequential access if (result){ pb_mysql_free_result(result); result = NULL; } return [self evaluateSQL:s useResult:useResult]; } - (unsigned)deleteRowsDescribedByQualifier:(EOQualifier *)q forTable:(PBDDTable *)t; { NSString *wc,*s; unsigned n; if(!t)return 0; wc = [_APP whereClauseFrom:q forTable:t]; if(FILLED(wc))wc = [NSSWF @" where %@",wc]; s = [NSSWF @"delete from %@ %@;",[t dbName],wc]; n = [self evaluateSQL:s]; //delete returned keinen resultset und muss daher auch nicht beenden [self endFetch]; return n; } - (NSMutableDictionary *)fetchRow; { PBDDAttribute *pba; NSString *s,*v; NSData *data; int i; int nelem; int len; long *lens; NSMutableDictionary *row=nil; if(![self isFetchInProgress])return nil; //wird nil returned, ist die Verbindung auch schon geschlossen if(!result){ [self endFetch]; LOGS(@"no result"); return nil; } if(!(cur = pb_mysql_fetch_row(result))){ char *mysql_error = pb_mysql_error(sock); if(mysql_error[0]){ LOG(([NSSWF @"--- **** MySQL-Error: %s",mysql_error])); } [self endFetch]; // ist normal LOGS(@"no pb_mysql_fetch_row(result)"); return nil; } if(!selectedAttributes){ [self endFetch]; LOGS(@"no selectedAttributes"); return nil; } lens = pb_mysql_fetch_lengths(result); nelem = pb_mysql_num_fields(result); row = [[NSMutableDictionary alloc]initWithCapacity:nelem]; //die strings wie sie aus der datenbank kommen; in eoFromDict werden evt. Konvertierungen z.B. f. Datum vorgenommen for (i = 0; i < nelem; i++){ pba = [selectedAttributes oai:i]; s = [pba dbName]; len = lens[i]; if (cur[i] == NULL){ [row setSecureObject:EON forKey:s]; continue; } data = [[NSData alloc]initWithBytes:cur[i] length:len]; v = [[NSString alloc] initWithData:data encoding:(isUTF8?NSUTF8StringEncoding:NSISOLatin1StringEncoding)]; [data release]; if(!v){ [row setSecureObject:EON forKey:s]; continue; } [row setSecureObject:v forKey:s]; [v release]; } //wozu? pb_mysql_field_seek(result, 0); return [row autorelease]; } - (void)cancelFetch; { [self endFetch]; } - (NSArray *)describeResults; { LMA; PBDDAttribute *pba; if(!result)return nil; while ((curField = pb_mysql_fetch_field(result))) { pba = [[PBDDAttribute alloc]init]; [pba setDbName:[NSString stringWithCString:curField->name]]; [pba setLength:curField->length]; [pba setDataTyp:DT_CHAR]; [lma addObject:pba]; [pba release]; } return lma; } - (unsigned)evaluateSQL:(NSString *)s; { return [self evaluateSQL:s useResult:NO]; //Default storeResult } - (unsigned)evaluateSQL:(NSString *)s useResult:(BOOL)useResult; { NSData *data; NSString *s1; int sql_rc; if(!FILLED(s))return 0; if([_APP orbDebug])LOGS(s); // if([_APP orbDebugStack])PRINTCURRENTSTACK; data = [s dataUsingEncoding:(isUTF8?NSUTF8StringEncoding:NSISOLatin1StringEncoding) allowLossyConversion:YES]; [_APP setLastSQLError:EON]; sql_rc = (pb_mysql_real_query(sock, (char *)[data bytes], [data length])); if(sql_rc){ // nach 8 h inaktivitaet (default) schliesst MySQL den Socket; wird dann nochmal der Channel verwendet, gibt es einen lost Connection error // danach geht der Channel aber wieder, allerdings mit Latin1 als Clien-characterset s1 = [NSSWF @"could not evaluate expression \"%@\"\n***Error: %i %s", s,sql_rc,pb_mysql_error(sock)]; LOGS((s1)); [_APP setLastSQLError:s1]; // PRINTCURRENTSTACK; [self closeChannel]; //damit er wieder geoeffnet werden muss und setNames laeuft; return 0; }else{ if(pb_mysql_field_count(sock)){ //sollte resultset haben if (result){ //evt. alten result-set weg pb_mysql_free_result(result); result = NULL; } //kein return 0 hier, da es auch statements gibt, die kein result liefern, z.B. "lock tables" if(useResult){ result = pb_mysql_use_result(sock); //zeile fuer zeile if(!result){ // if([_APP orbDebug])LOGS(@"no result"); } else if(result->eof){ // if([_APP orbDebug])LOGS(@"result->eof"); } }else{ result = pb_mysql_store_result(sock); //alles auf einmal auf den client if(!result){ // if([_APP orbDebug])LOGS(@"no result"); } } if(result)[self beginFetch]; } } return 1; } - (void)beginFetch; { // if([_APP orbDebug])LOGS(@"did beginFetch"); isFetchInProgress = YES; } - (void)endFetch; { if (isFetchInProgress) { isFetchInProgress = NO; [self setSelectedAttributes:nil]; if (result) { pb_mysql_free_result(result); result = NULL; } } } - (NSArray *)describeDatabase; { //die PBDDTables meines conDict liefern; //dieses muss aktiv sein und die Verbindung zur db muss stehen; NSArray *a = [self describeTableNames]; int i,j; LMA; NSString *tn; // LOGS(([NSSWF @"APP.utf8db = %@",[_APP utf8db]?@"J":@"N"])); for(i=0,j=[a count];idecimals; //nachkomma [pba setDataTyp:DT_FLOAT]; [pba setNak:nak]; [pba setLength:curField->length]; break; */ }else{ LOGS(([NSSWF @"unbekannter Datentyp:%@ in table %@:\n%@",type,tableName,[d description]])); [pba setDataTyp:DT_CHAR]; [pba setNak:0]; [pba setLength:20]; } if([[d ofk:@"Key"]iE:@"PRI"]){ [pba setKeyTyp:KT_PRIM]; } // add to list of attributes [lma addObject:pba]; [pba release]; } [self cancelFetch]; return lma; } - (BOOL)isUTF8; { return isUTF8; } @end