diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/CMCopyTable.h | 13 | ||||
-rw-r--r-- | Source/CMCopyTable.m | 38 | ||||
-rw-r--r-- | Source/SPCustomQuery.h | 9 | ||||
-rw-r--r-- | Source/SPCustomQuery.m | 194 | ||||
-rw-r--r-- | Source/SPDatabaseDocument.m | 10 | ||||
-rw-r--r-- | Source/SPTableContent.h | 2 | ||||
-rw-r--r-- | Source/SPTableContent.m | 33 |
7 files changed, 237 insertions, 62 deletions
diff --git a/Source/CMCopyTable.h b/Source/CMCopyTable.h index 948607bc..c7a6280a 100644 --- a/Source/CMCopyTable.h +++ b/Source/CMCopyTable.h @@ -25,7 +25,8 @@ #import <AppKit/AppKit.h> #import "SPTableView.h" -#define SP_MAX_CELL_WIDTH 200 +#define SP_MAX_CELL_WIDTH_MULTICOLUMN 200 +#define SP_MAX_CELL_WIDTH 400 @class SPDataStorage; @@ -115,7 +116,7 @@ - (void)setTableData:(SPDataStorage *)theTableStorage; /*! - @method autodetectColumnWidthsForFont: + @method autodetectColumnWidths @abstract Autodetect and return column widths based on contents @discussion Support autocalculating column widths for the represented data. This uses the underlying table storage, calculates string widths, @@ -123,13 +124,12 @@ Suitable for calling on background threads, but ensure that the data storage range in use (currently rows 1-200) won't be altered while this accesses it. - @param The font to use when calculating widths @result A dictionary - mapped by column identifier - of the column widths to use */ -- (NSDictionary *) autodetectColumnWidthsForFont:(NSFont *)theFont; +- (NSDictionary *) autodetectColumnWidths; /*! - @method autodetectWidthForColumnDefinition:usingFont:maxRows: + @method autodetectWidthForColumnDefinition:maxRows: @abstract Autodetect and return column width based on contents @discussion Support autocalculating column width for the represented data. This uses the underlying table storage, and the supplied column definition, @@ -138,7 +138,6 @@ Suitable for calling on background threads, but ensure that the data storage range in use won't be altered while being accessed. @param A column definition for a represented column; the column to use is derived - @param The font to use when calculating widths @param The maximum number of rows to process when looking at string lengths @result A reasonable column width to use when displaying data */ @@ -146,7 +145,7 @@ * Autodetect the column width for a specified column - derived from the supplied * column definition, using the stored data and the specified font. */ -- (NSUInteger)autodetectWidthForColumnDefinition:(NSDictionary *)columnDefinition usingFont:(NSFont *)theFont maxRows:(NSUInteger)rowsToCheck; +- (NSUInteger)autodetectWidthForColumnDefinition:(NSDictionary *)columnDefinition maxRows:(NSUInteger)rowsToCheck; @end diff --git a/Source/CMCopyTable.m b/Source/CMCopyTable.m index 44df07f6..909dfa3d 100644 --- a/Source/CMCopyTable.m +++ b/Source/CMCopyTable.m @@ -449,16 +449,45 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003; /** * Autodetect column widths for a specified font. */ -- (NSDictionary *) autodetectColumnWidthsForFont:(NSFont *)theFont; +- (NSDictionary *) autodetectColumnWidths { NSMutableDictionary *columnWidths = [NSMutableDictionary dictionaryWithCapacity:[columnDefinitions count]]; NSUInteger columnWidth; + NSUInteger allColumnWidths = 0; for (NSDictionary *columnDefinition in columnDefinitions) { if ([[NSThread currentThread] isCancelled]) return nil; - columnWidth = [self autodetectWidthForColumnDefinition:columnDefinition usingFont:theFont maxRows:100]; + columnWidth = [self autodetectWidthForColumnDefinition:columnDefinition maxRows:100]; [columnWidths setObject:[NSNumber numberWithUnsignedInteger:columnWidth] forKey:[columnDefinition objectForKey:@"datacolumnindex"]]; + allColumnWidths += columnWidth; + } + + // Compare the column widths to the table width. If wider, narrow down wide columns as necessary + if (allColumnWidths > [self bounds].size.width) { + NSUInteger availableWidthToReduce = 0; + + // Look for columns that are wider than the multi-column max + for (NSString *columnIdentifier in columnWidths) { + columnWidth = [[columnWidths objectForKey:columnIdentifier] unsignedIntegerValue]; + if (columnWidth > SP_MAX_CELL_WIDTH_MULTICOLUMN) availableWidthToReduce += columnWidth - SP_MAX_CELL_WIDTH_MULTICOLUMN; + } + + // Determine how much width can be reduced + NSUInteger widthToReduce = allColumnWidths - [self bounds].size.width; + if (availableWidthToReduce < widthToReduce) widthToReduce = availableWidthToReduce; + + // Proportionally decrease the column sizes + if (widthToReduce) { + NSArray *columnIdentifiers = [columnWidths allKeys]; + for (NSString *columnIdentifier in columnIdentifiers) { + columnWidth = [[columnWidths objectForKey:columnIdentifier] unsignedIntegerValue]; + if (columnWidth > SP_MAX_CELL_WIDTH_MULTICOLUMN) { + columnWidth -= ceil((double)(columnWidth - SP_MAX_CELL_WIDTH_MULTICOLUMN) / availableWidthToReduce * widthToReduce); + [columnWidths setObject:[NSNumber numberWithUnsignedInteger:columnWidth] forKey:columnIdentifier]; + } + } + } } return columnWidths; @@ -468,15 +497,16 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003; * Autodetect the column width for a specified column - derived from the supplied * column definition, using the stored data and the specified font. */ -- (NSUInteger)autodetectWidthForColumnDefinition:(NSDictionary *)columnDefinition usingFont:(NSFont *)theFont maxRows:(NSUInteger)rowsToCheck +- (NSUInteger)autodetectWidthForColumnDefinition:(NSDictionary *)columnDefinition maxRows:(NSUInteger)rowsToCheck { CGFloat columnBaseWidth; id contentString; NSUInteger cellWidth, maxCellWidth, i; NSRange linebreakRange; double rowStep; + NSFont *tableFont = [NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPGlobalResultTableFont]]; NSUInteger columnIndex = [[columnDefinition objectForKey:@"datacolumnindex"] unsignedIntegerValue]; - NSDictionary *stringAttributes = [NSDictionary dictionaryWithObject:theFont forKey:NSFontAttributeName]; + NSDictionary *stringAttributes = [NSDictionary dictionaryWithObject:tableFont forKey:NSFontAttributeName]; // Check the number of rows available to check, sampling every n rows if ([tableStorage count] < rowsToCheck) { diff --git a/Source/SPCustomQuery.h b/Source/SPCustomQuery.h index 5c95aeb8..149417b9 100644 --- a/Source/SPCustomQuery.h +++ b/Source/SPCustomQuery.h @@ -156,6 +156,9 @@ NSInteger currentHistoryOffsetIndex; BOOL historyItemWasJustInserted; + + NSTimer *queryLoadTimer; + NSUInteger queryLoadInterfaceUpdateInterval, queryLoadTimerTicksSinceLastUpdate, queryLoadLastRowCount; } // IBAction methods @@ -191,6 +194,11 @@ - (NSRange)queryTextRangeForQuery:(NSInteger)anIndex startPosition:(NSUInteger)position; - (void) updateStatusInterfaceWithDetails:(NSDictionary *)errorDetails; +// Query load actions +- (void) initQueryLoadTimer; +- (void) clearQueryLoadTimer; +- (void) queryLoadUpdate:(NSTimer *)theTimer; + // Accessors - (NSArray *)currentResult; - (void)processResultIntoDataStorage:(MCPStreamingResult *)theResult; @@ -203,6 +211,7 @@ - (void) setResultViewportToRestore:(NSRect)theViewport; - (void) storeCurrentResultViewForRestoration; - (void) clearResultViewDetailsToRestore; +- (void) autosizeColumns; // MySQL Help - (void)showAutoHelpForCurrentWord:(id)sender; diff --git a/Source/SPCustomQuery.m b/Source/SPCustomQuery.m index 6703ea5d..c2a480c6 100644 --- a/Source/SPCustomQuery.m +++ b/Source/SPCustomQuery.m @@ -603,6 +603,21 @@ [[self onMainThread] updateTableView]; } + // Find result table name for copying as SQL INSERT. + // If more than one table name is found set resultTableName to nil. + // resultTableName will be set to the original table name (not defined via AS) provided by mysql return + // and the resultTableName can differ due to case-sensitive/insensitive settings!. + NSString *resultTableName = [[cqColumnDefinition objectAtIndex:0] objectForKey:@"org_table"]; + for(id field in cqColumnDefinition) { + if(![[field objectForKey:@"org_table"] isEqualToString:resultTableName]) { + resultTableName = nil; + break; + } + } + + // Init copyTable with necessary information for copying selected rows as SQL INSERT + [customQueryView setTableInstance:self withTableData:resultData withColumns:cqColumnDefinition withTableName:resultTableName withConnection:mySQLConnection]; + [self processResultIntoDataStorage:streamingResult]; } else { [streamingResult cancelResultLoad]; @@ -794,18 +809,6 @@ return; } - // Find result table name for copying as SQL INSERT. - // If more than one table name is found set resultTableName to nil. - // resultTableName will be set to the original table name (not defined via AS) provided by mysql return - // and the resultTableName can differ due to case-sensitive/insensitive settings!. - NSString *resultTableName = [[cqColumnDefinition objectAtIndex:0] objectForKey:@"org_table"]; - for(id field in cqColumnDefinition) { - if(![[field objectForKey:@"org_table"] isEqualToString:resultTableName]) { - resultTableName = nil; - break; - } - } - [customQueryView reloadData]; // Restore the result view origin if appropriate @@ -824,9 +827,6 @@ tableRowsSelectable = previousTableRowsSelectable; } - // Init copyTable with necessary information for copying selected rows as SQL INSERT - [customQueryView setTableInstance:self withTableData:resultData withColumns:cqColumnDefinition withTableName:resultTableName withConnection:mySQLConnection]; - //query finished [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; @@ -852,10 +852,7 @@ - (void)processResultIntoDataStorage:(MCPStreamingResult *)theResult { NSArray *tempRow; - NSUInteger rowsProcessed = 0; - NSUInteger nextTableDisplayBoundary = 50; NSAutoreleasePool *dataLoadingPool; - BOOL tableViewRedrawn = NO; // Remove all items from the table resultDataCount = 0; @@ -864,6 +861,9 @@ [resultData removeAllRows]; pthread_mutex_unlock(&resultDataLock); + // Set up the table updates timer + [[self onMainThread] initQueryLoadTimer]; + // Set the column count on the data store [resultData setColumnCount:[theResult numOfFields]]; @@ -878,26 +878,19 @@ resultDataCount++; pthread_mutex_unlock(&resultDataLock); - // Update the count of rows processed - rowsProcessed++; - - // Update the table view with new results every now and then - if (rowsProcessed > nextTableDisplayBoundary) { - [customQueryView performSelectorOnMainThread:@selector(noteNumberOfRowsChanged) withObject:nil waitUntilDone:NO]; - if (!tableViewRedrawn) { - [customQueryView performSelectorOnMainThread:@selector(displayIfNeeded) withObject:nil waitUntilDone:NO]; - tableViewRedrawn = YES; - } - nextTableDisplayBoundary *= 2; - } - // Drain and reset the autorelease pool every ~1024 rows - if (!(rowsProcessed % 1024)) { + if (!(resultDataCount % 1024)) { [dataLoadingPool drain]; dataLoadingPool = [[NSAutoreleasePool alloc] init]; } } + // Clean up the interface update timer + [[self onMainThread] clearQueryLoadTimer]; + + // If the final column autoresize wasn't performed, perform it + if (queryLoadLastRowCount < 200) [[self onMainThread] autosizeColumns]; + [customQueryView performSelectorOnMainThread:@selector(noteNumberOfRowsChanged) withObject:nil waitUntilDone:NO]; [customQueryView setNeedsDisplay:YES]; @@ -1279,6 +1272,79 @@ } #pragma mark - +#pragma mark Table load actions + +/** + * Set up the table loading interface update timer. + * This should be called on the main thread. + */ +- (void) initQueryLoadTimer +{ + if (queryLoadTimer) [self clearTableLoadTimer]; + queryLoadInterfaceUpdateInterval = 1; + queryLoadLastRowCount = 0; + queryLoadTimerTicksSinceLastUpdate = 0; + + queryLoadTimer = [[NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(queryLoadUpdate:) userInfo:nil repeats:YES] retain]; +} + +/** + * Invalidate and release the table loading interface update timer. + * This should be called on the main thread. + */ +- (void) clearQueryLoadTimer +{ + if (queryLoadTimer) { + [queryLoadTimer invalidate]; + [queryLoadTimer release]; + queryLoadTimer = nil; + } +} + +/** + * Perform table interface updates when loading queries, based on timer + * ticks. As data becomes available, the table should be redrawn to + * show new rows - quickly at the start of the table, and then slightly + * slower after some time to avoid needless updates. + */ +- (void) queryLoadUpdate:(NSTimer *)theTimer +{ + if (queryLoadTimerTicksSinceLastUpdate < queryLoadInterfaceUpdateInterval) { + queryLoadTimerTicksSinceLastUpdate++; + return; + } + + // Check whether a table update is required, based on whether new rows are + // available to display. + if (resultDataCount == queryLoadLastRowCount) { + return; + } + + // Update the table display + [customQueryView noteNumberOfRowsChanged]; + if (!queryLoadLastRowCount) [customQueryView setNeedsDisplay:YES]; + + // Update column widths in two cases: on very first rows displayed, and once + // more than 200 rows are present. + if (queryLoadInterfaceUpdateInterval == 1 || (resultDataCount >= 200 && queryLoadLastRowCount < 200)) { + [self autosizeColumns]; + } + + queryLoadLastRowCount = resultDataCount; + + // Determine whether to decrease the update frequency + switch (queryLoadInterfaceUpdateInterval) { + case 1: + queryLoadInterfaceUpdateInterval = 10; + break; + case 10: + queryLoadInterfaceUpdateInterval = 25; + break; + } + queryLoadTimerTicksSinceLastUpdate = 0; +} + +#pragma mark - #pragma mark Accessors /* @@ -1479,6 +1545,29 @@ [self setResultViewportToRestore:NSZeroRect]; } +/** + * Autosize all columns based on their content. + * Should be called on the main thread. + */ +- (void)autosizeColumns +{ + if (isWorking) pthread_mutex_lock(&resultDataLock); + NSDictionary *columnWidths = [customQueryView autodetectColumnWidths]; + if (isWorking) pthread_mutex_unlock(&resultDataLock); + [customQueryView setDelegate:nil]; + for (NSDictionary *columnDefinition in cqColumnDefinition) { + + // Skip columns with saved widths + if ([[[[prefs objectForKey:SPTableColumnWidths] objectForKey:[NSString stringWithFormat:@"%@@%@", [tableDocumentInstance database], [tableDocumentInstance host]]] objectForKey:[tablesListInstance tableName]] objectForKey:[columnDefinition objectForKey:@"name"]]) continue; + + // Otherwise set the column width + NSTableColumn *aTableColumn = [customQueryView tableColumnWithIdentifier:[columnDefinition objectForKey:@"datacolumnindex"]]; + NSUInteger targetWidth = [[columnWidths objectForKey:[columnDefinition objectForKey:@"datacolumnindex"]] unsignedIntegerValue]; + [aTableColumn setWidth:targetWidth]; + } + [customQueryView setDelegate:self]; +} + #pragma mark - #pragma mark Field Editing @@ -2151,6 +2240,43 @@ [prefs setObject:tableColumnWidths forKey:SPTableColumnWidths]; } +/** + * Resize a column when it's double-clicked. (10.6+) + */ +- (CGFloat)tableView:(NSTableView *)tableView sizeToFitWidthOfColumn:(NSInteger)columnIndex +{ + NSTableColumn *theColumn = [[tableView tableColumns] objectAtIndex:columnIndex]; + NSDictionary *columnDefinition = [cqColumnDefinition objectAtIndex:[[theColumn identifier] integerValue]]; + + // Get the column width + NSUInteger targetWidth = [customQueryView autodetectWidthForColumnDefinition:columnDefinition maxRows:500]; + + // Clear any saved widths for the column + NSString *dbKey = [NSString stringWithFormat:@"%@@%@", [tableDocumentInstance database], [tableDocumentInstance host]]; + NSString *tableKey = [tablesListInstance tableName]; + NSMutableDictionary *savedWidths = [NSMutableDictionary dictionaryWithDictionary:[prefs objectForKey:SPTableColumnWidths]]; + NSMutableDictionary *dbDict = [NSMutableDictionary dictionaryWithDictionary:[savedWidths objectForKey:dbKey]]; + NSMutableDictionary *tableDict = [NSMutableDictionary dictionaryWithDictionary:[dbDict objectForKey:tableKey]]; + if ([tableDict objectForKey:[columnDefinition objectForKey:@"name"]]) { + [tableDict removeObjectForKey:[columnDefinition objectForKey:@"name"]]; + if ([tableDict count]) { + [dbDict setObject:[NSDictionary dictionaryWithDictionary:tableDict] forKey:tableKey]; + } else { + [dbDict removeObjectForKey:tableKey]; + } + if ([dbDict count]) { + [savedWidths setObject:[NSDictionary dictionaryWithDictionary:dbDict] forKey:dbKey]; + } else { + [savedWidths removeObjectForKey:dbKey]; + } + [prefs setObject:[NSDictionary dictionaryWithDictionary:savedWidths] forKey:SPTableColumnWidths]; + } + + // Return the width, while the delegate is empty to prevent column resize notifications + [customQueryView setDelegate:nil]; + [customQueryView performSelector:@selector(setDelegate:) withObject:self afterDelay:0.1]; + return targetWidth; +} #pragma mark - #pragma mark TextView delegate methods @@ -3176,6 +3302,8 @@ currentHistoryOffsetIndex = -1; historyItemWasJustInserted = NO; + queryLoadTimer = nil; + prefs = [NSUserDefaults standardUserDefaults]; } @@ -3294,7 +3422,9 @@ { [[NSNotificationCenter defaultCenter] removeObserver:self]; [prefs removeObserver:self forKeyPath:SPGlobalResultTableFont]; + [NSObject cancelPreviousPerformRequestsWithTarget:customQueryView]; + [self clearQueryLoadTimer]; [usedQuery release]; [resultData release]; [favoritesManager release]; diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m index 21d5034a..a31a9ac7 100644 --- a/Source/SPDatabaseDocument.m +++ b/Source/SPDatabaseDocument.m @@ -1387,10 +1387,14 @@ // Check the supplied progress. Compare it to the display interval - how often // the interface is updated - and update the interface if the value has changed enough. taskProgressValue = taskPercentage; - if (taskProgressValue > taskDisplayLastValue + taskProgressValueDisplayInterval - || taskProgressValue < taskDisplayLastValue - taskProgressValueDisplayInterval) + if (taskProgressValue >= taskDisplayLastValue + taskProgressValueDisplayInterval + || taskProgressValue <= taskDisplayLastValue - taskProgressValueDisplayInterval) { - [taskProgressIndicator performSelectorOnMainThread:@selector(setNumberValue:) withObject:[NSNumber numberWithDouble:taskProgressValue] waitUntilDone:NO]; + if ([NSThread isMainThread]) { + [taskProgressIndicator setDoubleValue:taskProgressValue]; + } else { + [taskProgressIndicator performSelectorOnMainThread:@selector(setNumberValue:) withObject:[NSNumber numberWithDouble:taskProgressValue] waitUntilDone:NO]; + } taskDisplayLastValue = taskProgressValue; } } diff --git a/Source/SPTableContent.h b/Source/SPTableContent.h index 1899d6a6..15059b6d 100644 --- a/Source/SPTableContent.h +++ b/Source/SPTableContent.h @@ -101,7 +101,7 @@ NSInteger paginationViewHeight; NSTimer *tableLoadTimer; - NSUInteger tableLoadInterfaceUpdateInterval, tableLoadTimerTicksSinceLastUpdate, tableLoadLastRowCount; + NSUInteger tableLoadInterfaceUpdateInterval, tableLoadTimerTicksSinceLastUpdate, tableLoadLastRowCount, tableLoadTargetRowCount; } // Table loading methods and information diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m index 26eeb568..795c9970 100644 --- a/Source/SPTableContent.m +++ b/Source/SPTableContent.m @@ -691,14 +691,13 @@ NSUInteger i; NSUInteger dataColumnsCount = [dataColumns count]; BOOL *columnBlobStatuses = malloc(dataColumnsCount * sizeof(BOOL)); + tableLoadTargetRowCount = targetRowCount; // Set up the table updates timer [[self onMainThread] initTableLoadTimer]; // Set the column count on the data store [tableValues setColumnCount:dataColumnsCount]; - - CGFloat relativeTargetRowCount = 100.0/targetRowCount; NSAutoreleasePool *dataLoadingPool; NSProgressIndicator *dataLoadingIndicator = [tableDocumentInstance valueForKey:@"queryProgressBar"]; @@ -735,16 +734,6 @@ pthread_mutex_unlock(&tableValuesLock); - // Update the task interface as necessary - if (!isFiltered) { - if (tableRowsCount < targetRowCount) { - [tableDocumentInstance setTaskPercentage:(tableRowsCount*relativeTargetRowCount)]; - } else if (tableRowsCount == targetRowCount) { - [tableDocumentInstance setTaskPercentage:100.0]; - [[tableDocumentInstance onMainThread] setTaskProgressToIndeterminateAfterDelay:YES]; - } - } - // Drain and reset the autorelease pool every ~1024 rows if (!(tableRowsCount % 1024)) { [dataLoadingPool drain]; @@ -1047,6 +1036,18 @@ */ - (void) tableLoadUpdate:(NSTimer *)theTimer { + + // Update the task interface as necessary + if (!isFiltered && tableLoadTargetRowCount != NSUIntegerMax) { + if (tableRowsCount < tableLoadTargetRowCount) { + [tableDocumentInstance setTaskPercentage:(tableRowsCount*100/tableLoadTargetRowCount)]; + } else if (tableRowsCount >= tableLoadTargetRowCount) { + [tableDocumentInstance setTaskPercentage:100.0]; + [tableDocumentInstance setTaskProgressToIndeterminateAfterDelay:YES]; + tableLoadTargetRowCount = NSUIntegerMax; + } + } + if (tableLoadTimerTicksSinceLastUpdate < tableLoadInterfaceUpdateInterval) { tableLoadTimerTicksSinceLastUpdate++; return; @@ -1064,7 +1065,7 @@ // Update column widths in two cases: on very first rows displayed, and once // more than 200 rows are present. - if (tableLoadInterfaceUpdateInterval || (tableRowsCount >= 200 && tableLoadLastRowCount < 200)) { + if (tableLoadInterfaceUpdateInterval == 1 || (tableRowsCount >= 200 && tableLoadLastRowCount < 200)) { [self autosizeColumns]; } @@ -2823,7 +2824,9 @@ */ - (void)autosizeColumns { - NSDictionary *columnWidths = [tableContentView autodetectColumnWidthsForFont:[NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPGlobalResultTableFont]]]; + if (isWorking) pthread_mutex_lock(&tableValuesLock); + NSDictionary *columnWidths = [tableContentView autodetectColumnWidths]; + if (isWorking) pthread_mutex_unlock(&tableValuesLock); [tableContentView setDelegate:nil]; for (NSDictionary *columnDefinition in dataColumns) { @@ -3255,7 +3258,7 @@ NSDictionary *columnDefinition = [dataColumns objectAtIndex:[[theColumn identifier] integerValue]]; // Get the column width - NSUInteger targetWidth = [tableContentView autodetectWidthForColumnDefinition:columnDefinition usingFont:[NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPGlobalResultTableFont]] maxRows:500]; + NSUInteger targetWidth = [tableContentView autodetectWidthForColumnDefinition:columnDefinition maxRows:500]; // Clear any saved widths for the column NSString *dbKey = [NSString stringWithFormat:@"%@@%@", [tableDocumentInstance database], [tableDocumentInstance host]]; |