diff options
-rw-r--r-- | Resources/Plists/PreferenceDefaults.plist | 2 | ||||
-rw-r--r-- | Source/SPAppController.h | 2 | ||||
-rw-r--r-- | Source/SPAppController.m | 77 | ||||
-rw-r--r-- | Source/SPBundleHTMLOutputController.h | 2 | ||||
-rw-r--r-- | Source/SPBundleHTMLOutputController.m | 68 | ||||
-rw-r--r-- | Source/SPCopyTable.m | 24 | ||||
-rw-r--r-- | Source/SPDatabaseDocument.m | 3 | ||||
-rw-r--r-- | Source/SPTableContent.h | 2 | ||||
-rw-r--r-- | Source/SPTableContent.m | 152 |
9 files changed, 215 insertions, 117 deletions
diff --git a/Resources/Plists/PreferenceDefaults.plist b/Resources/Plists/PreferenceDefaults.plist index 71fca976..093174a2 100644 --- a/Resources/Plists/PreferenceDefaults.plist +++ b/Resources/Plists/PreferenceDefaults.plist @@ -173,5 +173,7 @@ <true/> <key>FilterTableDefaultOperator</key> <string>LIKE '%@%'</string> + <key>WebKitDeveloperExtras</key> + <true/> </dict> </plist> diff --git a/Source/SPAppController.h b/Source/SPAppController.h index 0d68b26c..569ef2b8 100644 --- a/Source/SPAppController.h +++ b/Source/SPAppController.h @@ -113,7 +113,7 @@ - (void)handleEventWithURL:(NSURL*)url; - (IBAction)executeBundleItemForApp:(id)sender; -- (NSDictionary*)shellEnvironment; +- (NSDictionary*)shellEnvironmentForDocument:(NSString*)docUUID; - (void)addHTMLOutputController:(id)controller; diff --git a/Source/SPAppController.m b/Source/SPAppController.m index 841a4d69..e5b47c80 100644 --- a/Source/SPAppController.m +++ b/Source/SPAppController.m @@ -663,10 +663,10 @@ while(1) { NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:[NSDate distantPast] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if ([event type] == NSKeyDown) { unichar key = [[event characters] length] == 1 ? [[event characters] characterAtIndex:0] : 0; if (([event modifierFlags] & NSCommandKeyMask) && key == '.') { @@ -684,33 +684,50 @@ return; } - if(processDocument && command && [command isEqualToString:@"passToDoc"]) { - NSMutableDictionary *cmdDict = [NSMutableDictionary dictionary]; - [cmdDict setObject:parameter forKey:@"parameter"]; - [cmdDict setObject:passedProcessID forKey:@"id"]; - [processDocument handleSchemeCommand:cmdDict]; - return; + if(processDocument && command) { + if([command isEqualToString:@"passToDoc"]) { + NSMutableDictionary *cmdDict = [NSMutableDictionary dictionary]; + [cmdDict setObject:parameter forKey:@"parameter"]; + [cmdDict setObject:passedProcessID forKey:@"id"]; + [processDocument handleSchemeCommand:cmdDict]; + return; + } + else { + SPBeginAlertSheet(NSLocalizedString(@"sequelpro URL Scheme Error", @"sequelpro url Scheme Error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [NSApp mainWindow], self, nil, nil, + [NSString stringWithFormat:@"%@ “%@”:\n%@", NSLocalizedString(@"Error for", @"error for message"), [command description], NSLocalizedString(@"sequelpro URL scheme command not supported.", @"sequelpro URL scheme command not supported.")]); + + return; + } } - if(processDocument != nil) { + if(passedProcessID && [passedProcessID length]) { // If command failed notify the file handle hand shake mechanism NSString *out = @"1"; [out writeToFile:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, passedProcessID] atomically:YES encoding:NSUTF8StringEncoding error:nil]; - out = @"Scheme Error"; + out = NSLocalizedString(@"An error for sequelpro URL scheme command occurred. Probably no corresponding connection window found.", @"An error for sequelpro URL scheme command occurred. Probably no corresponding connection window found."); [out writeToFile:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, passedProcessID] atomically:YES encoding:NSUTF8StringEncoding error:nil]; - } + + SPBeginAlertSheet(NSLocalizedString(@"sequelpro URL Scheme Error", @"sequelpro url Scheme Error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [NSApp mainWindow], self, nil, nil, + [NSString stringWithFormat:@"%@ “%@”:\n%@", NSLocalizedString(@"Error for", @"error for message"), [command description], NSLocalizedString(@"An error for sequelpro URL scheme command occurred. Probably no corresponding connection window found.", @"An error for sequelpro URL scheme command occurred. Probably no corresponding connection window found.")]); + + + usleep(5000); + [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, passedProcessID] error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, passedProcessID] error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultMetaPathHeader, passedProcessID] error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryInputPathHeader, passedProcessID] error:nil]; - if(passedProcessID == nil || ![passedProcessID length]) { + + + } else { SPBeginAlertSheet(NSLocalizedString(@"sequelpro URL Scheme Error", @"sequelpro url Scheme Error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [NSApp mainWindow], self, nil, nil, [NSString stringWithFormat:@"%@ “%@”:\n%@", NSLocalizedString(@"Error for", @"error for message"), [command description], NSLocalizedString(@"An error occur while executing a scheme command. If the scheme command was invoked by a Bundle command, it could be that the command still runs. You can try to terminate it by pressing ⌘+. or via the Activities pane.", @"an error occur while executing a scheme command. if the scheme command was invoked by a bundle command, it could be that the command still runs. you can try to terminate it by pressing ⌘+. or via the activities pane.")]); - } else { - NSBeep(); } if(processDocument) @@ -720,6 +737,7 @@ NSLog(@"param: %@", parameter); NSLog(@"command: %@", command); NSLog(@"command id: %@", passedProcessID); + } - (IBAction)executeBundleItemForApp:(id)sender @@ -858,6 +876,7 @@ if([[win delegate] isKindOfClass:[SPBundleHTMLOutputController class]]) { if([[[win delegate] windowUUID] isEqualToString:[cmdData objectForKey:SPBundleFileUUIDKey]]) { correspondingWindowFound = YES; + [[win delegate] setDocUUID:uuid]; [[win delegate] displayHTMLContent:output withOptions:nil]; break; } @@ -866,6 +885,7 @@ if(!correspondingWindowFound) { SPBundleHTMLOutputController *c = [[SPBundleHTMLOutputController alloc] init]; [c setWindowUUID:[cmdData objectForKey:SPBundleFileUUIDKey]]; + [c setDocUUID:uuid]; [c displayHTMLContent:output withOptions:nil]; [[NSApp delegate] addHTMLOutputController:c]; } @@ -889,11 +909,30 @@ * Return of certain shell variables mainly for usage in JavaScript support inside the * HTML output window to allow to ask on run-time */ -- (NSDictionary*)shellEnvironment +- (NSDictionary*)shellEnvironmentForDocument:(NSString*)docUUID { NSMutableDictionary *env = [NSMutableDictionary dictionary]; - SPDatabaseDocument *doc = [self frontDocument]; - if(doc) [env addEntriesFromDictionary:[doc shellVariables]]; + SPDatabaseDocument *doc; + if(docUUID == nil) + doc = [self frontDocument]; + else { + BOOL found = NO; + for (NSWindow *aWindow in [NSApp orderedWindows]) { + if([[aWindow windowController] isMemberOfClass:[SPWindowController class]]) { + for(SPDatabaseDocument *d in [[aWindow windowController] documents]) { + if([d processID] && [[d processID] isEqualToString:docUUID]) { + [env addEntriesFromDictionary:[d shellVariables]]; + found = YES; + break; + } + } + } + if(found) break; + } + } + + // if(doc && [doc shellVariables]) [env addEntriesFromDictionary:[doc shellVariables]]; + // if(doc) [doc release]; id firstResponder = [[NSApp keyWindow] firstResponder]; if([firstResponder respondsToSelector:@selector(executeBundleItemForInputField:)]) { BOOL selfIsQueryEditor = ([[[firstResponder class] description] isEqualToString:@"SPTextView"]) ; diff --git a/Source/SPBundleHTMLOutputController.h b/Source/SPBundleHTMLOutputController.h index 0ad38241..ea0b96da 100644 --- a/Source/SPBundleHTMLOutputController.h +++ b/Source/SPBundleHTMLOutputController.h @@ -32,6 +32,7 @@ NSString *docTitle; NSString *initHTMLSourceString; NSString *windowUUID; + NSString *docUUID; WebPreferences *webPreferences; } @@ -39,6 +40,7 @@ @property(readwrite,retain) NSString *docTitle; @property(readwrite,retain) NSString *initHTMLSourceString; @property(readwrite,retain) NSString *windowUUID; +@property(readwrite,retain) NSString *docUUID; - (IBAction)printDocument:(id)sender; diff --git a/Source/SPBundleHTMLOutputController.m b/Source/SPBundleHTMLOutputController.m index a632a51a..7cad079b 100644 --- a/Source/SPBundleHTMLOutputController.m +++ b/Source/SPBundleHTMLOutputController.m @@ -25,11 +25,14 @@ #import "SPBundleHTMLOutputController.h" #import "SPAlertSheets.h" +@class WebScriptCallFrame; + @implementation SPBundleHTMLOutputController @synthesize docTitle; @synthesize initHTMLSourceString; @synthesize windowUUID; +@synthesize docUUID; /** * Initialisation @@ -63,7 +66,6 @@ { [[self window] orderFront:nil]; - [self setInitHTMLSourceString:content]; [[webView mainFrame] loadHTMLString:content baseURL:nil]; @@ -208,6 +210,7 @@ [webView close]; [self setInitHTMLSourceString:@""]; windowUUID = @""; + docUUID = @""; [self release]; } @@ -299,6 +302,20 @@ } } +- (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame +{ + if(error) { + NSLog(@"%@", [error localizedDescription]); + } +} + +- (void)webView:(WebView *)webView didFailLoadWithError:(NSError*)error forFrame:(WebFrame *)frame +{ + if(error) { + NSLog(@"%@", [error localizedDescription]); + } +} + #pragma mark - #pragma mark JS support @@ -328,9 +345,11 @@ return NO; } -- (void)webView:(WebView *)sender windowScriptObjectAvailable: (WebScriptObject *)windowScriptObject +- (void)webView:(WebView *)sender windowScriptObjectAvailable:(WebScriptObject *)windowScriptObject { + [windowScriptObject setValue:self forKey:@"system"]; + [webView setScriptDebugDelegate:self]; } + (NSString *)webScriptNameForSelector:(SEL)aSelector @@ -370,17 +389,40 @@ return YES; } -- (void)windowScriptObjectAvailable:(WebScriptObject*)webScriptObject { - [webScriptObject setValue:self forKey:@"system"]; +- (void)webView:(WebView *)webView failedToParseSource:(NSString *)source baseLineNumber:(NSUInteger)lineNumber fromURL:(NSURL *)url withError:(NSError *)error forWebFrame:(WebFrame *)webFrame +{ + NSString *mes = [NSString stringWithFormat:@"Failed to parse JavaScript source:\nline = %ld\nerror = %@ with\n%@\nfor source = \n%@", lineNumber, [error localizedDescription], [error userInfo], source]; + + NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"JavaScript Parsing Error", @"javascript parsing error") + defaultButton:NSLocalizedString(@"OK", @"OK button") + alternateButton:nil + otherButton:nil + informativeTextWithFormat:mes]; + + [alert setAlertStyle:NSCriticalAlertStyle]; + [alert runModal]; } +- (void)webView:(WebView *)webView exceptionWasRaised:(WebScriptCallFrame *)frame sourceId:(NSInteger)sid line:(NSInteger)lineno forWebFrame:(WebFrame *)webFrame +{ + NSString *mes = [NSString stringWithFormat:@"Exception:\nline = %ld\nfunction = %@\ncaller = %@\nexception = %@", lineno, [frame functionName], [frame caller], [frame userInfo], [frame exception]]; + + NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"JavaScript Exception", @"javascript exception") + defaultButton:NSLocalizedString(@"OK", @"OK button") + alternateButton:nil + otherButton:nil + informativeTextWithFormat:mes]; + + [alert setAlertStyle:NSCriticalAlertStyle]; + [alert runModal]; +} /** * JavaScript window.system.getShellEnvironmentForName('a_key') function to * return the value for key keyName */ - (NSString *)getShellEnvironmentForName:(NSString*)keyName { - return [[[NSApp delegate] shellEnvironment] objectForKey:keyName]; + return [[[NSApp delegate] shellEnvironmentForDocument:nil] objectForKey:keyName]; } /** @@ -403,6 +445,9 @@ NSString *command = nil; NSString *uuid = nil; + if([self docUUID] && [[self docUUID] length]) + uuid = [self docUUID]; + if([call isKindOfClass:[NSString class]]) command = [NSString stringWithString:call]; else if([[[call class] description] isEqualToString:@"WebScriptObject"]){ @@ -426,8 +471,15 @@ NSString *output = nil; if(uuid == nil) output = [command runBashCommandWithEnvironment:nil atCurrentDirectoryPath:nil error:&err]; - else - output = [command runBashCommandWithEnvironment:nil + else { + NSMutableDictionary *theEnv = [NSMutableDictionary dictionary]; + [theEnv addEntriesFromDictionary:[[NSApp delegate] shellEnvironmentForDocument:nil]]; + [theEnv setObject:uuid forKey:SPBundleShellVariableProcessID]; + [theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryInputPathHeader, uuid] forKey:SPBundleShellVariableQueryFile]; + [theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, uuid] forKey:SPBundleShellVariableQueryResultFile]; + [theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, uuid] forKey:SPBundleShellVariableQueryResultStatusFile]; + [theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultMetaPathHeader, uuid] forKey:SPBundleShellVariableQueryResultMetaFile]; + output = [command runBashCommandWithEnvironment:theEnv atCurrentDirectoryPath:nil callerInstance:[NSApp delegate] contextInfo:[NSDictionary dictionaryWithObjectsAndKeys: @@ -436,7 +488,7 @@ uuid, SPBundleFileInternalexecutionUUID, nil] error:&err]; - + } if(err != nil) { NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Error while executing JavaScript BASH command", @"error while executing javascript bash command") diff --git a/Source/SPCopyTable.m b/Source/SPCopyTable.m index 7a132348..6b9383ab 100644 --- a/Source/SPCopyTable.m +++ b/Source/SPCopyTable.m @@ -973,8 +973,8 @@ NSInteger kBlobAsImageFile = 4; // Save the current line if it's the last field in the table if ( [self numberOfColumns] - 1 == column ) { - if([[self delegate] respondsToSelector:@selector(addRowToDB)]) - [[self delegate] addRowToDB]; + if([[self delegate] respondsToSelector:@selector(saveRowToTable)]) + [[self delegate] saveRowToTable]; [[self window] makeFirstResponder:self]; } else { // Select the next field for editing @@ -991,8 +991,8 @@ NSInteger kBlobAsImageFile = 4; // Save the current line if it's the last field in the table if ( column < 1 ) { - if([[self delegate] respondsToSelector:@selector(addRowToDB)]) - [[self delegate] addRowToDB]; + if([[self delegate] respondsToSelector:@selector(saveRowToTable)]) + [[self delegate] saveRowToTable]; [[self window] makeFirstResponder:self]; } else { // Select the previous field for editing @@ -1010,8 +1010,8 @@ NSInteger kBlobAsImageFile = 4; return YES; [[control window] makeFirstResponder:control]; - if([[self delegate] isKindOfClass:[SPTableContent class]] && ![self isCellEditingMode] && [[self delegate] respondsToSelector:@selector(addRowToDB)]) - [[self delegate] addRowToDB]; + if([[self delegate] isKindOfClass:[SPTableContent class]] && ![self isCellEditingMode] && [[self delegate] respondsToSelector:@selector(saveRowToTable)]) + [[self delegate] saveRowToTable]; return YES; } @@ -1028,10 +1028,10 @@ NSInteger kBlobAsImageFile = 4; if (newRow>=[[self delegate] numberOfRowsInTableView:self]) return YES; //check if we're already at the end of the list [[control window] makeFirstResponder:control]; - if([[self delegate] isKindOfClass:[SPTableContent class]] && ![self isCellEditingMode] && [[self delegate] respondsToSelector:@selector(addRowToDB)]) - [[self delegate] addRowToDB]; + if([[self delegate] isKindOfClass:[SPTableContent class]] && ![self isCellEditingMode] && [[self delegate] respondsToSelector:@selector(saveRowToTable)]) + [[self delegate] saveRowToTable]; - if (newRow>=[[self delegate] numberOfRowsInTableView:self]) return YES; //check again. addRowToDB could reload the table and change the number of rows + if (newRow>=[[self delegate] numberOfRowsInTableView:self]) return YES; //check again. saveRowToTable could reload the table and change the number of rows if (tableStorage && column>=[tableStorage columnCount]) return YES; //the column count could change too [self selectRowIndexes:[NSIndexSet indexSetWithIndex:newRow] byExtendingSelection:NO]; @@ -1051,10 +1051,10 @@ NSInteger kBlobAsImageFile = 4; NSUInteger newRow = row-1; [[control window] makeFirstResponder:control]; - if([[self delegate] isKindOfClass:[SPTableContent class]] && ![self isCellEditingMode] && [[self delegate] respondsToSelector:@selector(addRowToDB)]) - [[self delegate] addRowToDB]; + if([[self delegate] isKindOfClass:[SPTableContent class]] && ![self isCellEditingMode] && [[self delegate] respondsToSelector:@selector(saveRowToTable)]) + [[self delegate] saveRowToTable]; - if (newRow>=[[self delegate] numberOfRowsInTableView:self]) return YES; // addRowToDB could reload the table and change the number of rows + if (newRow>=[[self delegate] numberOfRowsInTableView:self]) return YES; // saveRowToTable could reload the table and change the number of rows if (tableStorage && column>=[tableStorage columnCount]) return YES; //the column count could change too [self selectRowIndexes:[NSIndexSet indexSetWithIndex:newRow] byExtendingSelection:NO]; diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m index a65dab2b..44d3965c 100644 --- a/Source/SPDatabaseDocument.m +++ b/Source/SPDatabaseDocument.m @@ -4816,6 +4816,9 @@ - (NSDictionary*)shellVariables { + + if(!_isConnected) return [NSDictionary dictionary]; + NSMutableDictionary *env = [NSMutableDictionary dictionary]; if (tablesListInstance) { diff --git a/Source/SPTableContent.h b/Source/SPTableContent.h index 1dc4caf9..e3fd8a53 100644 --- a/Source/SPTableContent.h +++ b/Source/SPTableContent.h @@ -197,7 +197,7 @@ - (void)clickLinkArrowTask:(SPTextAndLinkCell *)theArrowCell; - (IBAction)setCompareTypes:(id)sender; - (void)processResultIntoDataStorage:(MCPStreamingResult *)theResult approximateRowCount:(NSUInteger)targetRowCount; -- (BOOL)addRowToDB; +- (BOOL)saveRowToTable; - (NSString *)argumentForRow:(NSInteger)row; - (NSString *)argumentForRow:(NSInteger)row excludingLimits:(BOOL)excludeLimits; - (NSString *)argumentForRow:(NSUInteger)rowIndex ofTable:(NSString *)tableForColumn andDatabase:(NSString *)database includeBlobs:(BOOL)includeBlobs; diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m index 1e9bdf5f..82282b5d 100644 --- a/Source/SPTableContent.m +++ b/Source/SPTableContent.m @@ -2370,116 +2370,116 @@ /** - * Tries to write a new row to the database. - * Returns YES if row is written to database, otherwise NO; also returns YES if no row - * is being edited and nothing has to be written to the database. + * Tries to write a new row to the table. + * Returns YES if row is written to table, otherwise NO; also returns YES if no row + * is being edited or nothing has to be written to the table. */ -- (BOOL)addRowToDB +- (BOOL)saveRowToTable { - if([tablesListInstance tableType] == SPTableTypeView) return; + // Only handle tables - views should be handled per-cell. + if ([tablesListInstance tableType] == SPTableTypeView) return NO; - NSMutableString *queryString; - id rowObject; - NSMutableString *rowValue = [NSMutableString string]; - NSString *currentTime = [[NSDate date] descriptionWithCalendarFormat:@"%H:%M:%S" timeZone:nil locale:nil]; - BOOL prefsLoadBlobsAsNeeded = [prefs boolForKey:SPLoadBlobsAsNeeded]; - NSInteger i; - - if ( !isEditingRow || currentlyEditingRow == -1) { - return YES; - } - - [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance]; + // If no row is being edited, return success. + if (!isEditingRow) return YES; - // If editing, compare the new row to the old row and if they are identical finish editing without saving. + // If editing, quickly compare the new row to the old row and if they are identical finish editing without saving. if (!isEditingNewRow && [oldRow isEqualToArray:[tableValues rowContentsAtIndex:currentlyEditingRow]]) { isEditingRow = NO; currentlyEditingRow = -1; - [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; return YES; } - NSMutableArray *fieldValues = [[NSMutableArray alloc] init]; - - // Get the field values - for ( i = 0 ; i < [dataColumns count] ; i++ ) { + // Iterate through the row contents, constructing the (ordered) arrays of keys and values to be saved + NSMutableArray *rowFieldsToSave = [[NSMutableArray alloc] initWithCapacity:[dataColumns count]]; + NSMutableArray *rowValuesToSave = [[NSMutableArray alloc] initWithCapacity:[dataColumns count]]; + NSInteger i; + id rowObject; + for (i = 0; i < [dataColumns count]; i++) { rowObject = [tableValues cellDataAtRow:currentlyEditingRow column:i]; - // Add not loaded placeholders directly for easy comparison when added - if (prefsLoadBlobsAsNeeded && !isEditingNewRow && [rowObject isSPNotLoaded]) - { - [fieldValues addObject:[SPNotLoaded notLoaded]]; - continue; - - // Catch CURRENT_TIMESTAMP automatic updates - if the row is new and the cell value matches - // the default value, or if the cell hasn't changed, update the current timestamp. - } else if ([[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"onupdatetimestamp"] integerValue] - && ( (isEditingNewRow && [rowObject isEqualTo:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"default"]]) - || (!isEditingNewRow && [rowObject isEqualTo:NSArrayObjectAtIndex(oldRow, i)]))) - { - [rowValue setString:@"CURRENT_TIMESTAMP"]; + // Skip "not loaded" cells entirely - these only occur when editing tables when the + // preference setting is enabled, and don't need to be saved back to the table. + if ([rowObject isSPNotLoaded]) continue; + + // If an edit has taken place, and the field value hasn't changed, the value + // can also be skipped + if (!isEditingNewRow && [rowObject isEqual:NSArrayObjectAtIndex(oldRow, i)]) continue; - } else if ( [rowObject isNSNull] - || ([rowObject isMemberOfClass:[NSString class]] && [[rowObject description] isEqualToString:@""]) ) { + // Prepare to derive the value to save + NSString *fieldValue; + NSString *fieldTypeGroup = [NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"typegrouping"]; - //NULL when user entered the nullValue string defined in the prefs or when a number field isn't set - // problem: when a number isn't set, sequel-pro enters 0 - // -> second if argument isn't necessary! - [rowValue setString:@"NULL"]; - } else if ( [[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"typegrouping"] isEqualToString:@"geometry"] ) { - [rowValue setString:([rowObject isKindOfClass:[MCPGeometryData class]]) ? [[rowObject wktString] getGeomFromTextString] : [(NSString*)rowObject getGeomFromTextString]]; + // Use NULL when the user has entered the nullValue string defined in the preferences, + // or when a numeric field is empty. + if ([rowObject isNSNull] + || (([fieldTypeGroup isEqualToString:@"float"] || [fieldTypeGroup isEqualToString:@"integer"]) + && [[rowObject description] isEqualToString:@""])) + { + fieldValue = @"NULL"; + + // Convert geometry values to their string values + } else if ([fieldTypeGroup isEqualToString:@"geometry"]) { + fieldValue = ([rowObject isKindOfClass:[MCPGeometryData class]]) ? [[rowObject wktString] getGeomFromTextString] : [(NSString*)rowObject getGeomFromTextString]; + // Convert the object to a string (here we can add special treatment for date-, number- and data-fields) } else { - // I don't believe any of these class matches are ever met at present. - if ( [rowObject isKindOfClass:[NSCalendarDate class]] ) { - [rowValue setString:[NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[rowObject description]]]]; - } else if ( [rowObject isKindOfClass:[NSNumber class]] ) { - [rowValue setString:[rowObject stringValue]]; - } else if ( [rowObject isKindOfClass:[NSData class]] ) { - [rowValue setString:[NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:rowObject]]]; + // I believe these class matches are not ever met at present. + if ([rowObject isKindOfClass:[NSCalendarDate class]]) { + fieldValue = [NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[rowObject description]]]; + } else if ([rowObject isKindOfClass:[NSNumber class]]) { + fieldValue = [rowObject stringValue]; + + // Convert data to its hex representation + } else if ([rowObject isKindOfClass:[NSData class]]) { + fieldValue = [NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:rowObject]]; + } else { if ([[rowObject description] isEqualToString:@"CURRENT_TIMESTAMP"]) { - [rowValue setString:@"CURRENT_TIMESTAMP"]; - } else if ([[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"typegrouping"] isEqualToString:@"bit"]) { - [rowValue setString:[NSString stringWithFormat:@"b'%@'", ((![[rowObject description] length] || [[rowObject description] isEqualToString:@"0"]) ? @"0" : [rowObject description])]]; - } else if ([[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"typegrouping"] isEqualToString:@"date"] - && [[rowObject description] isEqualToString:@"NOW()"]) { - [rowValue setString:@"NOW()"]; + fieldValue = @"CURRENT_TIMESTAMP"; + } else if ([fieldTypeGroup isEqualToString:@"bit"]) { + fieldValue = [NSString stringWithFormat:@"b'%@'", ((![[rowObject description] length] || [[rowObject description] isEqualToString:@"0"]) ? @"0" : [rowObject description])]; + } else if ([fieldTypeGroup isEqualToString:@"date"] && [[rowObject description] isEqualToString:@"NOW()"]) { + fieldValue = @"NOW()"; } else { - [rowValue setString:[NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[rowObject description]]]]; + fieldValue = [NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[rowObject description]]]; } } } - [fieldValues addObject:[NSString stringWithString:rowValue]]; + + // Store the key and value in the ordered arrays for saving. + [rowFieldsToSave addObject:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"]]; + [rowValuesToSave addObject:fieldValue]; } - // Use INSERT syntax when creating new rows - no need to do not loaded checking, as all values have been entered - if ( isEditingNewRow ) { + [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance]; + NSMutableString *queryString; + + // Use INSERT syntax when creating new rows + if (isEditingNewRow) { queryString = [NSString stringWithFormat:@"INSERT INTO %@ (%@) VALUES (%@)", - [selectedTable backtickQuotedString], [[tableDataInstance columnNames] componentsJoinedAndBacktickQuoted], [fieldValues componentsJoinedByString:@","]]; - // Use UPDATE syntax otherwise + [selectedTable backtickQuotedString], [rowFieldsToSave componentsJoinedAndBacktickQuoted], [rowValuesToSave componentsJoinedByString:@", "]]; + + // Otherwise use an UPDATE syntax to save only the changed cells - if this point is reached, + // the equality test has failed and so there is always at least one changed cell } else { - BOOL firstCellOutput = NO; queryString = [NSMutableString stringWithFormat:@"UPDATE %@ SET ", [selectedTable backtickQuotedString]]; - for ( i = 0 ; i < [dataColumns count] ; i++ ) { - - // If data column loading is deferred and the value is the not loaded string, skip this cell - if (prefsLoadBlobsAsNeeded && [[fieldValues objectAtIndex:i] isSPNotLoaded]) continue; - - if (firstCellOutput) [queryString appendString:@", "]; - else firstCellOutput = YES; - + for (i = 0; i < [rowFieldsToSave count]; i++) { + if (i) [queryString appendString:@", "]; [queryString appendFormat:@"%@ = %@", - [[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"] backtickQuotedString], [fieldValues objectAtIndex:i]]; + [NSArrayObjectAtIndex(rowFieldsToSave, i) backtickQuotedString], + NSArrayObjectAtIndex(rowValuesToSave, i)]; } [queryString appendFormat:@" WHERE %@", [self argumentForRow:-2]]; } + [rowFieldsToSave release]; + [rowValuesToSave release]; + + // Run the query [mySQLConnection queryString:queryString]; - [fieldValues release]; [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; // If no rows have been changed, show error if appropriate. @@ -2494,7 +2494,7 @@ isEditingRow = NO; isEditingNewRow = NO; currentlyEditingRow = -1; - [[SPQueryController sharedQueryController] showErrorInConsole:[NSString stringWithFormat:NSLocalizedString(@"/* WARNING %@ No rows have been affected */\n", @"warning shown in the console when no rows have been affected after writing to the db"), currentTime] connection:[tableDocumentInstance name]]; + [[SPQueryController sharedQueryController] showErrorInConsole:NSLocalizedString(@"/* WARNING: No rows have been affected */\n", @"warning shown in the console when no rows have been affected after writing to the db") connection:[tableDocumentInstance name]]; return YES; // On success... @@ -2582,7 +2582,7 @@ isSavingRow = YES; // Attempt to save the row, and return YES if the save succeeded. - if ([self addRowToDB]) { + if ([self saveRowToTable]) { isSavingRow = NO; return YES; } |