From d607a28b78cd0256eff3cb71a2d33646a5b6d131 Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Mon, 13 Dec 2010 02:00:25 +0000 Subject: - When saving rows in the table content view, only include those fields which have altered values in the UPDATE query, improving speed and reducing the chance of issues when copying queries or altering rapidly changing tables. This addresses Issue #527; thanks to Tobias Mollstam for contributing a patch which prompted this update. - Rename SPTableContent's "addRowToDB" to "saveRowToTable" for clarity, and clean up the code for speed and clarity - Correctly save numeric fields as NULL if the value is blanked --- Source/SPCopyTable.m | 24 ++++---- Source/SPTableContent.h | 2 +- Source/SPTableContent.m | 155 +++++++++++++++++++++++++----------------------- 3 files changed, 95 insertions(+), 86 deletions(-) (limited to 'Source') 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/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..1f4889d7 100644 --- a/Source/SPTableContent.m +++ b/Source/SPTableContent.m @@ -2370,116 +2370,125 @@ /** - * 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; + // 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; + + // Prepare to derive the value to save, also tracking whether the field has changed. + BOOL fieldValueHasChanged = (isEditingNewRow || ![rowObject isEqual:NSArrayObjectAtIndex(oldRow, i)]); + NSString *fieldValue; + NSString *fieldTypeGroup = [NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"typegrouping"]; // 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)]))) + 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"]; - - } else if ( [rowObject isNSNull] - || ([rowObject isMemberOfClass:[NSString class]] && [[rowObject description] isEqualToString:@""]) ) { + fieldValue = @"CURRENT_TIMESTAMP"; + fieldValueHasChanged = YES; + + // Use NULL when the user has entered the nullValue string defined in the preferences, + // or when a numeric field is empty. + } else if ([rowObject isNSNull] + || (([fieldTypeGroup isEqualToString:@"float"] || [fieldTypeGroup isEqualToString:@"integer"]) + && [[rowObject description] isEqualToString:@""])) + { + fieldValue = @"NULL"; - //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]]; + // 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]]; + + // If the field value hasn't changed (only occurs while editing!), continue without saving + if (!fieldValueHasChanged) continue; + + // 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 +2503,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 +2591,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; } -- cgit v1.2.3 From 3adfcf0898f78a8459aedd46be5e89ec906f0314 Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Mon, 13 Dec 2010 02:25:06 +0000 Subject: - Remove the CURRENT_TIMESTAMP workaround when saving rows as r3013 removes the need for it; this also improves speed slightly as non-changed rows can be skipped much earlier --- Source/SPTableContent.m | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) (limited to 'Source') diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m index 1f4889d7..82282b5d 100644 --- a/Source/SPTableContent.m +++ b/Source/SPTableContent.m @@ -2402,25 +2402,19 @@ // preference setting is enabled, and don't need to be saved back to the table. if ([rowObject isSPNotLoaded]) continue; - // Prepare to derive the value to save, also tracking whether the field has changed. - BOOL fieldValueHasChanged = (isEditingNewRow || ![rowObject isEqual:NSArrayObjectAtIndex(oldRow, i)]); + // 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; + + // Prepare to derive the value to save NSString *fieldValue; NSString *fieldTypeGroup = [NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"typegrouping"]; - // 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. - if ([[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"onupdatetimestamp"] integerValue] - && ( (isEditingNewRow && [rowObject isEqualTo:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"default"]]) - || (!isEditingNewRow && [rowObject isEqualTo:NSArrayObjectAtIndex(oldRow, i)]))) - { - fieldValue = @"CURRENT_TIMESTAMP"; - fieldValueHasChanged = YES; - // Use NULL when the user has entered the nullValue string defined in the preferences, // or when a numeric field is empty. - } else if ([rowObject isNSNull] - || (([fieldTypeGroup isEqualToString:@"float"] || [fieldTypeGroup isEqualToString:@"integer"]) - && [[rowObject description] isEqualToString:@""])) + if ([rowObject isNSNull] + || (([fieldTypeGroup isEqualToString:@"float"] || [fieldTypeGroup isEqualToString:@"integer"]) + && [[rowObject description] isEqualToString:@""])) { fieldValue = @"NULL"; @@ -2454,9 +2448,6 @@ } } - // If the field value hasn't changed (only occurs while editing!), continue without saving - if (!fieldValueHasChanged) continue; - // Store the key and value in the ordered arrays for saving. [rowFieldsToSave addObject:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"]]; [rowValuesToSave addObject:fieldValue]; -- cgit v1.2.3 From 3205740f8679f3408a3d5e4246d943b74db80f47 Mon Sep 17 00:00:00 2001 From: Bibiko Date: Mon, 13 Dec 2010 09:13:35 +0000 Subject: =?UTF-8?q?=E2=80=A2=20HTML=20output=20window=20-=20a=20BASH=20scr?= =?UTF-8?q?ipt=20called=20via=20window.system.run()=20inherits=20the=20cur?= =?UTF-8?q?rent=20shell=20vars=20and=20the=20associated=20SP=5FPROCESS=5FI?= =?UTF-8?q?D=20if=20set=20to=20make=20it=20easier=20to=20run=20sequelpro?= =?UTF-8?q?=20URL=20scheme=20commands=20from=20JavaScript=20via=20BASH=20-?= =?UTF-8?q?=20improved=20error=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/SPAppController.h | 2 +- Source/SPAppController.m | 77 ++++++++++++++++++++++++++--------- Source/SPBundleHTMLOutputController.h | 2 + Source/SPBundleHTMLOutputController.m | 22 +++++++--- Source/SPDatabaseDocument.m | 3 ++ 5 files changed, 81 insertions(+), 25 deletions(-) (limited to 'Source') 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 cf36d161..043ea9bf 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..8a5b66e6 100644 --- a/Source/SPBundleHTMLOutputController.m +++ b/Source/SPBundleHTMLOutputController.m @@ -30,6 +30,7 @@ @synthesize docTitle; @synthesize initHTMLSourceString; @synthesize windowUUID; +@synthesize docUUID; /** * Initialisation @@ -63,7 +64,6 @@ { [[self window] orderFront:nil]; - [self setInitHTMLSourceString:content]; [[webView mainFrame] loadHTMLString:content baseURL:nil]; @@ -208,6 +208,7 @@ [webView close]; [self setInitHTMLSourceString:@""]; windowUUID = @""; + docUUID = @""; [self release]; } @@ -330,6 +331,7 @@ - (void)webView:(WebView *)sender windowScriptObjectAvailable: (WebScriptObject *)windowScriptObject { + [windowScriptObject setValue:self forKey:@"system"]; } @@ -380,7 +382,7 @@ */ - (NSString *)getShellEnvironmentForName:(NSString*)keyName { - return [[[NSApp delegate] shellEnvironment] objectForKey:keyName]; + return [[[NSApp delegate] shellEnvironmentForDocument:nil] objectForKey:keyName]; } /** @@ -403,6 +405,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 +431,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 +448,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/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m index 144f0710..7cea8eab 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) { -- cgit v1.2.3 From 2acbdc592b04e98fe5cacfedfe4638ef71d644ff Mon Sep 17 00:00:00 2001 From: Bibiko Date: Mon, 13 Dec 2010 13:24:40 +0000 Subject: =?UTF-8?q?=E2=80=A2=20HTML=20output=20window=20-=20enabled=20WebI?= =?UTF-8?q?nspector=20in=20context=20menu=20for=20debugging=20-=20alert=20?= =?UTF-8?q?the=20user=20about=20JavaScript=20parsing=20errors=20and=20exce?= =?UTF-8?q?ptions=20with=20some=20debug=20info?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/SPBundleHTMLOutputController.m | 46 ++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) (limited to 'Source') diff --git a/Source/SPBundleHTMLOutputController.m b/Source/SPBundleHTMLOutputController.m index 8a5b66e6..7cad079b 100644 --- a/Source/SPBundleHTMLOutputController.m +++ b/Source/SPBundleHTMLOutputController.m @@ -25,6 +25,8 @@ #import "SPBundleHTMLOutputController.h" #import "SPAlertSheets.h" +@class WebScriptCallFrame; + @implementation SPBundleHTMLOutputController @synthesize docTitle; @@ -300,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 @@ -329,10 +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 @@ -372,10 +389,33 @@ 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 -- cgit v1.2.3