aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2010-01-17 16:24:13 +0000
committerrowanbeentje <rowan@beent.je>2010-01-17 16:24:13 +0000
commitab9b7d9dad3dcd3a2675d33684729637e5509d42 (patch)
treeb1655480f033fdcbbd12caed945fcead636042dc
parentfc6348bb6cadfedba1fd19615f2af83d8e8098ae (diff)
downloadsequelpro-ab9b7d9dad3dcd3a2675d33684729637e5509d42.tar.gz
sequelpro-ab9b7d9dad3dcd3a2675d33684729637e5509d42.tar.bz2
sequelpro-ab9b7d9dad3dcd3a2675d33684729637e5509d42.zip
Add a new SPDataStorage class, and use it in TableContent and CustomQuery:
- SPDataStorage is a class designed for a 2D array of fixed-width data storage, replacing the current method of nested NSMutableArrays. NSFastEnumerator compatible. - Overall memory overhead for table storage in memory reduced by 1.2-1.4x - this almost gains back the large memory jump seen for 64 bit - Some operations (adding data, retrieving a single cell's data) are faster than nested NSMutableArrays; some operations (requesting a row as an NSArray) are slightly slower as the data needs to be converted, but overall result is a slight speed gain. (- Could be used in future to store SQL results in C datatypes, avoiding very high NSObject overhead for numbers and short strings)
-rw-r--r--Source/CMCopyTable.h3
-rw-r--r--Source/CMCopyTable.m4
-rw-r--r--Source/CustomQuery.h8
-rw-r--r--Source/CustomQuery.m64
-rw-r--r--Source/SPDataStorage.h101
-rw-r--r--Source/SPDataStorage.m424
-rw-r--r--Source/TableContent.h5
-rw-r--r--Source/TableContent.m118
-rw-r--r--sequel-pro.xcodeproj/project.pbxproj6
9 files changed, 627 insertions, 106 deletions
diff --git a/Source/CMCopyTable.h b/Source/CMCopyTable.h
index aede3540..95dd0c4b 100644
--- a/Source/CMCopyTable.h
+++ b/Source/CMCopyTable.h
@@ -36,7 +36,6 @@
@interface CMCopyTable : SPTableView
{
id tableInstance; // the table content view instance
- id tableData; // the actual table data source
id mySQLConnection; // current MySQL connection
NSArray* columnDefinitions; // array of NSDictionary containing info about columns
NSString* selectedTable; // the name of the current selected table
@@ -103,7 +102,7 @@
/*
* Set all necessary data from the table content view.
*/
-- (void)setTableInstance:(id)anInstance withTableData:(id)theTableData withColumns:(NSArray *)columnDefs withTableName:(NSString *)aTableName withConnection:(id)aMySqlConnection;
+- (void)setTableInstance:(id)anInstance withColumns:(NSArray *)columnDefs withTableName:(NSString *)aTableName withConnection:(id)aMySqlConnection;
@end
diff --git a/Source/CMCopyTable.m b/Source/CMCopyTable.m
index 6fd83cee..621fc08f 100644
--- a/Source/CMCopyTable.m
+++ b/Source/CMCopyTable.m
@@ -232,7 +232,6 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2002;
rowCounter++;
for ( c = 0; c < numColumns; c++ )
{
- // rowData = [[tableData objectAtIndex:rowIndex] objectAtIndex:[[columnMappings objectAtIndex:c] integerValue]];
col = NSArrayObjectAtIndex(columns, c);
rowData = [dataSource tableView:self
objectValueForTableColumn:col
@@ -417,10 +416,9 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2002;
/*
* Init self with data coming from the table content view. Mainly used for copying data properly.
*/
-- (void)setTableInstance:(id)anInstance withTableData:(id)theTableData withColumns:(NSArray *)columnDefs withTableName:(NSString *)aTableName withConnection:(id)aMySqlConnection
+- (void)setTableInstance:(id)anInstance withColumns:(NSArray *)columnDefs withTableName:(NSString *)aTableName withConnection:(id)aMySqlConnection
{
selectedTable = aTableName;
- tableData = theTableData;
mySQLConnection = aMySqlConnection;
tableInstance = anInstance;
diff --git a/Source/CustomQuery.h b/Source/CustomQuery.h
index 1c9a32bf..8e2e9fdd 100644
--- a/Source/CustomQuery.h
+++ b/Source/CustomQuery.h
@@ -49,7 +49,7 @@
#define SP_HISTORY_SAVE_MENUITEM_TAG 300001
#define SP_HISTORY_CLEAR_MENUITEM_TAG 300002
-@class SPQueryFavoriteManager;
+@class SPQueryFavoriteManager, SPDataStorage;
@interface CustomQuery : NSObject
{
@@ -128,9 +128,9 @@
WebHistory *helpHistory;
NSString *helpHTMLTemplate;
- NSMutableArray *fullResult;
- pthread_mutex_t fullResultLock;
- NSInteger fullResultCount;
+ SPDataStorage *resultData;
+ pthread_mutex_t resultDataLock;
+ NSInteger resultDataCount;
NSArray *cqColumnDefinition;
NSString *lastExecutedQuery;
NSInteger editedRow;
diff --git a/Source/CustomQuery.m b/Source/CustomQuery.m
index de035270..e35a7b26 100644
--- a/Source/CustomQuery.m
+++ b/Source/CustomQuery.m
@@ -41,6 +41,7 @@
#import "SPQueryController.h"
#import "SPConstants.h"
#import "SPEncodingPopupAccessory.h"
+#import "SPDataStorage.h"
@implementation CustomQuery
@@ -779,7 +780,7 @@
[tableDocumentInstance setQueryMode:SPInterfaceQueryMode];
// If no results were returned, redraw the empty table and post notifications before returning.
- if ( !fullResultCount ) {
+ if ( !resultDataCount ) {
[customQueryView reloadData];
if (streamingResult) [streamingResult release];
@@ -831,7 +832,7 @@
}
// Init copyTable with necessary information for copying selected rows as SQL INSERT
- [customQueryView setTableInstance:self withTableData:fullResult withColumns:cqColumnDefinition withTableName:resultTableName withConnection:mySQLConnection];
+ [customQueryView setTableInstance:self withColumns:cqColumnDefinition withTableName:resultTableName withConnection:mySQLConnection];
//query finished
[[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
@@ -864,11 +865,14 @@
BOOL tableViewRedrawn = NO;
// Remove all items from the table
- fullResultCount = 0;
+ resultDataCount = 0;
[customQueryView performSelectorOnMainThread:@selector(noteNumberOfRowsChanged) withObject:nil waitUntilDone:YES];
- pthread_mutex_lock(&fullResultLock);
- [fullResult removeAllObjects];
- pthread_mutex_unlock(&fullResultLock);
+ pthread_mutex_lock(&resultDataLock);
+ [resultData removeAllRows];
+ pthread_mutex_unlock(&resultDataLock);
+
+ // Set the column count on the data store
+ [resultData setColumnCount:[theResult numOfFields]];
// Set up an autorelease pool for row processing
dataLoadingPool = [[NSAutoreleasePool alloc] init];
@@ -876,10 +880,10 @@
// Loop through the result rows as they become available
while (tempRow = [theResult fetchNextRowAsArray]) {
- pthread_mutex_lock(&fullResultLock);
- NSMutableArrayAddObject(fullResult, [NSMutableArray arrayWithArray:tempRow]);
- fullResultCount++;
- pthread_mutex_unlock(&fullResultLock);
+ pthread_mutex_lock(&resultDataLock);
+ SPDataStorageAddRow(resultData, tempRow);
+ resultDataCount++;
+ pthread_mutex_unlock(&resultDataLock);
// Update the count of rows processed
rowsProcessed++;
@@ -1334,7 +1338,7 @@
[fieldIDQueryStr setString:@"WHERE ("];
// --- Build WHERE clause ---
- dataRow = [fullResult objectAtIndex:rowIndex];
+ dataRow = [resultData rowContentsAtIndex:rowIndex];
// Get the primary key if there is one
MCPResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@.%@",
@@ -1394,7 +1398,7 @@
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
if (aTableView == customQueryView) {
- return (fullResult == nil) ? 0 : fullResultCount;
+ return (resultData == nil) ? 0 : resultDataCount;
}
else {
return 0;
@@ -1417,21 +1421,18 @@
// locks must be used to avoid crashes, and indexes higher than the available
// rows or columns may be requested. Use gray to show loading in these cases.
if (isWorking) {
- pthread_mutex_lock(&fullResultLock);
- if (rowIndex < fullResultCount) {
- NSMutableArray *rowData = NSArrayObjectAtIndex(fullResult, rowIndex);
- if (columnIndex < [rowData count]) {
- theValue = NSArrayObjectAtIndex(rowData, columnIndex);
- }
+ pthread_mutex_lock(&resultDataLock);
+ if (rowIndex < resultDataCount && columnIndex < [resultData columnCount]) {
+ theValue = SPDataStorageObjectAtRowAndColumn(resultData, rowIndex, columnIndex);
}
- pthread_mutex_unlock(&fullResultLock);
+ pthread_mutex_unlock(&resultDataLock);
if (!theValue) {
[cell setTextColor:[NSColor lightGrayColor]];
return;
}
} else {
- theValue = NSArrayObjectAtIndex(NSArrayObjectAtIndex(fullResult, rowIndex), columnIndex);
+ theValue = SPDataStorageObjectAtRowAndColumn(resultData, rowIndex, columnIndex);
}
[cell setTextColor:[theValue isNSNull] ? [NSColor lightGrayColor] : [NSColor blackColor]];
@@ -1453,18 +1454,15 @@
// rows or columns may be requested. Return "..." to indicate loading in these
// cases.
if (isWorking) {
- pthread_mutex_lock(&fullResultLock);
- if (rowIndex < fullResultCount) {
- NSMutableArray *rowData = NSArrayObjectAtIndex(fullResult, rowIndex);
- if (columnIndex < [rowData count]) {
- theValue = NSArrayObjectAtIndex(rowData, columnIndex);
- }
+ pthread_mutex_lock(&resultDataLock);
+ if (rowIndex < resultDataCount && columnIndex < [resultData columnCount]) {
+ theValue = SPDataStorageObjectAtRowAndColumn(resultData, rowIndex, columnIndex);
}
- pthread_mutex_unlock(&fullResultLock);
+ pthread_mutex_unlock(&resultDataLock);
if (!theValue) return @"...";
} else {
- theValue = NSArrayObjectAtIndex(NSArrayObjectAtIndex(fullResult, rowIndex), columnIndex);
+ theValue = SPDataStorageObjectAtRowAndColumn(resultData, rowIndex, columnIndex);
}
if ([theValue isKindOfClass:[NSData class]])
@@ -1791,7 +1789,7 @@
// possible exceptions (eg for reloading tables etc.)
id theValue;
@try{
- theValue = NSArrayObjectAtIndex(NSArrayObjectAtIndex(fullResult, row), [[aTableColumn identifier] integerValue]);
+ theValue = SPDataStorageObjectAtRowAndColumn(resultData, row, [[aTableColumn identifier] integerValue]);
}
@catch(id ae) {
return nil;
@@ -1891,7 +1889,7 @@
&& [columnDefinition valueForKey:@"char_length"])
[fieldEditor setTextMaxLength:[[columnDefinition valueForKey:@"char_length"] integerValue]];
- id originalData = [[fullResult objectAtIndex:rowIndex] objectAtIndex:[[aTableColumn identifier] integerValue]];
+ id originalData = [resultData cellDataAtRow:rowIndex column:[[aTableColumn identifier] integerValue]];
if ([originalData isNSNull]) originalData = [prefs objectForKey:SPNullValue];
id editData = [[fieldEditor editWithObject:originalData
@@ -2976,8 +2974,8 @@
[[helpWebView backForwardList] setCapacity:20];
// init tableView's data source
- fullResultCount = 0;
- fullResult = [[NSMutableArray alloc] init];
+ resultDataCount = 0;
+ resultData = [[SPDataStorage alloc] init];
editedRow = -1;
prefs = [NSUserDefaults standardUserDefaults];
@@ -3083,7 +3081,7 @@
[[NSNotificationCenter defaultCenter] removeObserver:self];
[usedQuery release];
- [fullResult release];
+ [resultData release];
[favoritesManager release];
if (helpHTMLTemplate) [helpHTMLTemplate release];
diff --git a/Source/SPDataStorage.h b/Source/SPDataStorage.h
new file mode 100644
index 00000000..2baa8d03
--- /dev/null
+++ b/Source/SPDataStorage.h
@@ -0,0 +1,101 @@
+//
+// $Id$
+//
+// SPDataStorage.h
+// sequel-pro
+//
+// Created by Rowan Beentje on 10/01/2009.
+// Copyright 2009 Rowan Beentje. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import <Cocoa/Cocoa.h>
+
+/*
+ * This class provides a storage mechanism intended to represent tabular
+ * data, in a 2D array. Data can be added and retrieved either directly
+ * or via NSArrays; internally, C arrays are used to provide speed and
+ * memory improvements.
+ * This class is essentially mutable.
+ */
+
+#pragma mark -
+#pragma mark Class definition
+
+@interface SPDataStorage : NSObject {
+ NSUInteger numColumns;
+ NSUInteger columnPointerByteSize;
+ NSUInteger numRows, numRowsCapacity;
+
+ id **dataStorage;
+}
+
+/* Retrieving rows and cells */
+- (NSMutableArray *) rowContentsAtIndex:(NSUInteger)index;
+- (id) cellDataAtRow:(NSUInteger)rowIndex column:(NSUInteger)columnIndex;
+
+/* Adding and amending rows and cells */
+- (void) addRowWithContents:(NSArray *)row;
+- (void) insertRowContents:(NSArray *)row atIndex:(NSUInteger)index;
+- (void) replaceRowAtIndex:(NSUInteger)index withRowContents:(NSArray *)row;
+- (void) replaceObjectInRow:(NSUInteger)rowIndex column:(NSUInteger)columnIndex withObject:(id)object;
+- (void) removeRowAtIndex:(NSUInteger)index;
+- (void) removeRowsInRange:(NSRange)rangeToRemove;
+- (void) removeAllRows;
+
+/* Basic information */
+- (NSUInteger) count;
+- (void) setColumnCount:(NSUInteger)columnCount;
+- (NSUInteger) columnCount;
+
+/* Initialisation and teardown */
+#pragma mark -
+- (id) init;
+- (void) dealloc;
+
+@end
+
+#pragma mark -
+#pragma mark Cached method calls to remove obj-c messaging overhead in tight loops
+
+static inline void SPDataStorageAddRow(SPDataStorage* self, NSArray* row) {
+ typedef void (*SPDSAddRowMethodPtr)(SPDataStorage*, SEL, NSArray*);
+ static SPDSAddRowMethodPtr SPDSAddRow;
+ if (!SPDSAddRow) SPDSAddRow = (SPDSAddRowMethodPtr)[self methodForSelector:@selector(addRowWithContents:)];
+ SPDSAddRow(self, @selector(addRowWithContents:), row);
+}
+
+static inline void SPDataStorageReplaceRow(SPDataStorage* self, NSUInteger rowIndex, NSArray* row) {
+ typedef void (*SPDSReplaceRowMethodPtr)(SPDataStorage*, SEL, NSUInteger, NSArray*);
+ static SPDSReplaceRowMethodPtr SPDSReplaceRow;
+ if (!SPDSReplaceRow) SPDSReplaceRow = (SPDSReplaceRowMethodPtr)[self methodForSelector:@selector(replaceRowAtIndex:withRowContents:)];
+ SPDSReplaceRow(self, @selector(replaceRowAtIndex:withRowContents:), rowIndex, row);
+}
+
+static inline void SPDataStorageReplaceObjectAtRowAndColumn(SPDataStorage* self, NSUInteger rowIndex, NSUInteger colIndex, id newObject) {
+ typedef void (*SPDSObjectReplaceMethodPtr)(SPDataStorage*, SEL, NSUInteger, NSUInteger, id);
+ static SPDSObjectReplaceMethodPtr SPDSObjectReplace;
+ if (!SPDSObjectReplace) SPDSObjectReplace = (SPDSObjectReplaceMethodPtr)[self methodForSelector:@selector(replaceObjectInRow:column:withObject:)];
+ SPDSObjectReplace(self, @selector(replaceObjectInRow:column:withObject:), rowIndex, colIndex, newObject);
+}
+
+static inline id SPDataStorageObjectAtRowAndColumn(SPDataStorage* self, NSUInteger rowIndex, NSUInteger colIndex) {
+ typedef id (*SPDSObjectFetchMethodPtr)(SPDataStorage*, SEL, NSUInteger, NSUInteger);
+ static SPDSObjectFetchMethodPtr SPDSObjectFetch;
+ if (!SPDSObjectFetch) SPDSObjectFetch = (SPDSObjectFetchMethodPtr)[self methodForSelector:@selector(cellDataAtRow:column:)];
+ return SPDSObjectFetch(self, @selector(cellDataAtRow:column:), rowIndex, colIndex);
+}
diff --git a/Source/SPDataStorage.m b/Source/SPDataStorage.m
new file mode 100644
index 00000000..3db5cdbe
--- /dev/null
+++ b/Source/SPDataStorage.m
@@ -0,0 +1,424 @@
+//
+// $Id$
+//
+// SPDataStorage.m
+// sequel-pro
+//
+// Created by Rowan Beentje on 10/01/2009.
+// Copyright 2009 Rowan Beentje. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "SPDataStorage.h"
+
+@interface SPDataStorage (PrivateAPI)
+
+- (void) _ensureCapacityForAdditionalRowCount:(NSUInteger)numExtraRows;
+- (void) _increaseCapacity;
+
+@end
+
+
+@implementation SPDataStorage
+
+static inline void SPDataStorageEnsureCapacityForAdditionalRowCount(SPDataStorage* self, NSUInteger numExtraRows) {
+ typedef void (*SPDSEnsureCapacityMethodPtr)(SPDataStorage*, SEL, NSUInteger);
+ static SPDSEnsureCapacityMethodPtr SPDSEnsureCapacity;
+ if (!SPDSEnsureCapacity) SPDSEnsureCapacity = (SPDSEnsureCapacityMethodPtr)[self methodForSelector:@selector(_ensureCapacityForAdditionalRowCount:)];
+ SPDSEnsureCapacity(self, @selector(_ensureCapacityForAdditionalRowCount:), numExtraRows);
+}
+
+#pragma mark -
+#pragma mark Retrieving rows and cells
+
+/**
+ * Return a mutable array containing the data for a specified row.
+ */
+- (NSMutableArray *) rowContentsAtIndex:(NSUInteger)index
+{
+
+ // Throw an exception if the index is out of bounds
+ if (index >= numRows) [NSException raise:NSRangeException format:@"Requested storage index beyond bounds"];
+
+ // Construct the NSMutableArray
+ NSMutableArray *rowArray = [NSMutableArray arrayWithCapacity:numColumns];
+ id *row = dataStorage[index];
+ NSUInteger i;
+ for (i = 0; i < numColumns; i++) {
+ CFArrayAppendValue((CFMutableArrayRef)rowArray, row[i]);
+ }
+
+ return rowArray;
+}
+
+/**
+ * Return the data at a specified row and column index.
+ */
+- (id) cellDataAtRow:(NSUInteger)rowIndex column:(NSUInteger)columnIndex
+{
+
+ // Throw an exception if the row or column index is out of bounds
+ if (rowIndex >= numRows || columnIndex >= numColumns) [NSException raise:NSRangeException format:@"Requested storage index beyond bounds"];
+
+ // Return the content
+ return dataStorage[rowIndex][columnIndex];
+}
+
+#pragma mark -
+#pragma mark Retrieving rows via NSFastEnumeration
+
+/**
+ * Implementation of the NSFastEnumeration protocol.
+ * Note that this currently doesn't implement mutation guards.
+ */
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
+{
+
+ // If the start index is out of bounds, return 0 to indicate end of results
+ if (state->state >= numRows) return 0;
+
+ // Determine how many objects to return - 128, len, or all items remaining
+ NSUInteger itemsToReturn = 128;
+ if (len < 128) itemsToReturn = len;
+ if (numRows - state->state < itemsToReturn) {
+ itemsToReturn = numRows - state->state;
+ }
+
+ // Construct the arrays to return
+ NSUInteger i, j;
+ NSMutableArray *rowArray;
+ id *row;
+ for (i = 0; i < itemsToReturn; i++) {
+ row = dataStorage[state->state + i];
+ rowArray = [NSMutableArray arrayWithCapacity:numColumns];
+ for (j = 0; j < numColumns; j++) {
+ CFArrayAppendValue((CFMutableArrayRef)rowArray, row[j]);
+ }
+ stackbuf[i] = rowArray;
+ }
+
+ state->state += itemsToReturn;
+ state->itemsPtr = stackbuf;
+ state->mutationsPtr = (unsigned long *)&numRows;
+ return itemsToReturn;
+}
+
+#pragma mark -
+#pragma mark Adding and amending rows and cells
+
+/**
+ * Add a new row to the end of the storage array, supplying an NSArray
+ * of objects. Note that the supplied objects are retained as a reference
+ * rather than copied.
+ */
+- (void) addRowWithContents:(NSArray *)row
+{
+
+ // Ensure that sufficient capacity is available
+ SPDataStorageEnsureCapacityForAdditionalRowCount(self, 1);
+
+ // Add an empty row array to the data store
+ id *newRow = (id *)malloc(columnPointerByteSize);
+ dataStorage[numRows] = newRow;
+ numRows++;
+
+ // Copy over references to the array contents, and retain the objects
+ NSUInteger cellsCopied = 0;
+ for (id cellData in row) {
+ if (cellData) newRow[cellsCopied] = (id)CFRetain(cellData);
+ else newRow[cellsCopied] = nil;
+ if (++cellsCopied == numColumns) break;
+ }
+
+ // If an array shorter than the row width was added, pad with nils
+ if (cellsCopied < numColumns) {
+ for ( ; cellsCopied <= numColumns; cellsCopied++) newRow[cellsCopied] = nil;
+ }
+}
+
+/**
+ * Insert a new row into the storage array at a specified point, pushing
+ * all later rows the next index. Note that the supplied objects within the
+ * array are retained as a reference rather than copied.
+ */
+- (void) insertRowContents:(NSArray *)row atIndex:(NSUInteger)index
+{
+
+ // Throw an exception if the index is out of bounds
+ if (index > numRows) [NSException raise:NSRangeException format:@"Requested storage index beyond bounds"];
+
+ // If "inserting" at the end of the array just add a row
+ if (index == numRows) return SPDataStorageAddRow(self, row);
+
+ // Ensure that sufficient capacity is available to hold all the rows
+ SPDataStorageEnsureCapacityForAdditionalRowCount(self, 1);
+
+ // Renumber the specified index, and all subsequent indices, to create a gap
+ for (NSUInteger j = numRows - 1; j >= index; j--) {
+ dataStorage[j + 1] = dataStorage[j];
+ }
+
+ // Add a new instantiated row array to the data store at the specified point
+ id *newRow = (id *)malloc(columnPointerByteSize);
+ dataStorage[index] = newRow;
+ numRows++;
+
+ // Copy over references to the array contents, and retain the objects
+ NSUInteger cellsCopied = 0;
+ for (id cellData in row) {
+ if (cellData) newRow[cellsCopied] = (id)CFRetain(cellData);
+ else newRow[cellsCopied] = nil;
+ if (++cellsCopied == numColumns) break;
+ }
+
+ // If an array shorter than the row width was inserted, pad with nils
+ if (cellsCopied < numColumns) {
+ for ( ; cellsCopied <= numColumns; cellsCopied++) newRow[cellsCopied] = nil;
+ }
+}
+
+/**
+ * Replace a row with contents of the supplied NSArray.
+ */
+- (void) replaceRowAtIndex:(NSUInteger)index withRowContents:(NSArray *)row
+{
+ NSUInteger cellsProcessed = 0;
+
+ // Throw an exception if the index is out of bounds
+ if (index >= numRows) [NSException raise:NSRangeException format:@"Requested storage index beyond bounds"];
+
+ id *storageRow = dataStorage[index];
+
+ // Iterate through the row replacing the objects
+ for (id cellData in row) {
+ if (storageRow[cellsProcessed]) CFRelease(storageRow[cellsProcessed]);
+ if (cellData) storageRow[cellsProcessed] = (id)CFRetain(cellData);
+ else storageRow[cellsProcessed] = nil;
+ if (++cellsProcessed == numColumns) break;
+ }
+
+ // Ensure all cells are correctly updated if an array shorter than the row width was supplied
+ if (cellsProcessed < numColumns) {
+ for ( ; cellsProcessed <= numColumns; cellsProcessed++) {
+ if (storageRow[cellsProcessed]) CFRelease(storageRow[cellsProcessed]);
+ storageRow[cellsProcessed] = nil;
+ }
+ }
+}
+
+/**
+ * Replace the contents of a single cell with a supplied object.
+ */
+- (void) replaceObjectInRow:(NSUInteger)rowIndex column:(NSUInteger)columnIndex withObject:(id)object
+{
+
+ // Throw an exception of either index is out of bounds
+ if (rowIndex >= numRows || columnIndex >= numColumns) [NSException raise:NSRangeException format:@"Requested storage index beyond bounds"];
+
+ // Release the old object and retain the new one
+ if (dataStorage[rowIndex][columnIndex]) CFRelease(dataStorage[rowIndex][columnIndex]);
+ if (object) dataStorage[rowIndex][columnIndex] = (id)CFRetain(object);
+ else dataStorage[rowIndex][columnIndex] = nil;
+}
+
+/**
+ * Remove a row, renumbering all elements beyond index.
+ */
+- (void) removeRowAtIndex:(NSUInteger)index
+{
+
+ // Throw an exception if the index is out of bounds
+ if (index >= numRows) [NSException raise:NSRangeException format:@"Requested storage index beyond bounds"];
+
+ // Free the row
+ NSUInteger j = numColumns;
+ id *row = dataStorage[index];
+ while (j > 0) {
+ if (row[--j]) CFRelease(row[j]);
+ }
+ free(row);
+ numRows--;
+
+ // Renumber all subsequent indices to fill the gap
+ for (j = index; j < numRows; j++) {
+ dataStorage[j] = dataStorage[j + 1];
+ }
+ dataStorage[numRows] = NULL;
+}
+
+/**
+ * Remove all rows in the specified range, renumbering all elements
+ * beyond the end of the range.
+ */
+- (void) removeRowsInRange:(NSRange)rangeToRemove
+{
+
+ // Throw an exception if the range is out of bounds
+ if (rangeToRemove.location + rangeToRemove.length >= numRows) [NSException raise:NSRangeException format:@"Requested storage index beyond bounds"];
+
+ // Free rows in the range
+ NSUInteger i, j = numColumns;
+ id *row;
+ for (i = rangeToRemove.location; i < rangeToRemove.location + rangeToRemove.length; i++) {
+ row = dataStorage[i];
+ while (j > 0) {
+ if (row[--j]) CFRelease(row[j]);
+ }
+ free(row);
+ }
+ numRows -= rangeToRemove.length;
+
+ // Renumber all subsequent indices to fill the gap
+ for (i = rangeToRemove.location + rangeToRemove.length - 1; i < numRows; i++) {
+ dataStorage[i] = dataStorage[i + rangeToRemove.length];
+ }
+ for (i = numRows; i < numRows + rangeToRemove.length; i++) {
+ dataStorage[i] = NULL;
+ }
+}
+
+/**
+ * Remove all rows from the array, and free their associated memory.
+ */
+- (void) removeAllRows
+{
+ NSUInteger j;
+ id *row;
+
+ // Free all the data
+ while (numRows > 0) {
+ row = dataStorage[--numRows];
+ j = numColumns;
+ while (j > 0) {
+ if (row[--j]) CFRelease(row[j]);
+ }
+ free(row);
+ }
+
+ numRows = 0;
+}
+
+#pragma mark -
+#pragma mark Basic information
+
+/**
+ * Returns the number of rows currently held in data storage.
+ */
+- (NSUInteger) count
+{
+ return numRows;
+}
+
+/**
+ * Set the number of columns represented by the data storage.
+ */
+- (void) setColumnCount:(NSUInteger)columnCount
+{
+ columnPointerByteSize = columnCount * sizeof(id);
+
+ // If there are rows present in the storage, and the number of
+ // columns has changed, amend the existing rows to match.
+ if (columnCount != numColumns && numRows) {
+ NSUInteger i = numRows, j;
+ id *row;
+
+ // If the new column count is higher than the old count, iterate through the existing rows
+ // and pad with nils
+ if (columnCount > numColumns) {
+ while (i > 0) {
+ row = dataStorage[--i];
+ row = (id *)realloc(row, columnPointerByteSize);
+ j = numColumns;
+ while (j < columnCount) {
+ row[j++] = nil;
+ }
+ }
+
+ // If the new column count is lower than the old count, iterate through the existing rows
+ // freeing any extra objects
+ } else {
+ while (i > 0) {
+ row = dataStorage[--i];
+ j = numColumns;
+ while (j > columnCount) {
+ if (row[--j]) CFRelease(row[j]);
+ }
+ row = (id *)realloc(row, columnPointerByteSize);
+ }
+ }
+ }
+
+ // Update the column count
+ numColumns = columnCount;
+}
+
+/**
+ * Return the number of columns represented by the data storage.
+ */
+- (NSUInteger) columnCount
+{
+ return numColumns;
+}
+
+/**
+ * Setup and teardown
+ */
+#pragma mark -
+
+- (id) init {
+ if (self = [super init]) {
+ numColumns = 0;
+ columnPointerByteSize = 0;
+ numRows = 0;
+
+ // Initialise the array, initially with space for 100 rows
+ numRowsCapacity = 100;
+ dataStorage = (id **)malloc(numRowsCapacity * sizeof(id *));
+ }
+ return self;
+}
+
+- (void) dealloc {
+ [self removeAllRows];
+ free(dataStorage);
+ [super dealloc];
+}
+
+@end
+
+@implementation SPDataStorage (PrivateAPI)
+
+/**
+ * Private method to ensure the array always has sufficient capacity
+ * to store any additional rows required.
+ */
+- (void) _ensureCapacityForAdditionalRowCount:(NSUInteger)numExtraRows
+{
+ while (numRows + numExtraRows > numRowsCapacity) [self _increaseCapacity];
+}
+
+/**
+ * Private method to increase the storage available for the array;
+ * currently doubles the capacity as boundaries are reached.
+ */
+- (void) _increaseCapacity
+{
+ numRowsCapacity *= 2;
+ dataStorage = (id **)realloc(dataStorage, numRowsCapacity * sizeof(id *));
+}
+
+@end
diff --git a/Source/TableContent.h b/Source/TableContent.h
index c467b865..1b9e4af6 100644
--- a/Source/TableContent.h
+++ b/Source/TableContent.h
@@ -28,7 +28,7 @@
#import <Cocoa/Cocoa.h>
#import <MCPKit/MCPKit.h>
-@class CMCopyTable, SPTextAndLinkCell, SPHistoryController, SPTableInfo;
+@class CMCopyTable, SPTextAndLinkCell, SPHistoryController, SPTableInfo, SPDataStorage;
@interface TableContent : NSObject
{
@@ -72,7 +72,8 @@
pthread_mutex_t tableValuesLock;
NSString *selectedTable, *usedQuery;
- NSMutableArray *tableValues, *dataColumns, *keys, *oldRow;
+ SPDataStorage *tableValues;
+ NSMutableArray *dataColumns, *keys, *oldRow;
NSUInteger tableRowsCount, previousTableRowsCount;
NSString *compareType;
NSNumber *sortCol;
diff --git a/Source/TableContent.m b/Source/TableContent.m
index 5f3f7759..eb74accc 100644
--- a/Source/TableContent.m
+++ b/Source/TableContent.m
@@ -48,6 +48,7 @@
#import "SPContentFilterManager.h"
#import "SPNotLoaded.h"
#import "SPConstants.h"
+#import "SPDataStorage.h"
#import "TableDocument.h"
@implementation TableContent
@@ -62,7 +63,7 @@
isWorking = NO;
pthread_mutex_init(&tableValuesLock, NULL);
- tableValues = [[NSMutableArray alloc] init];
+ tableValues = [[SPDataStorage alloc] init];
tableRowsCount = 0;
previousTableRowsCount = 0;
dataColumns = [[NSMutableArray alloc] init];
@@ -221,7 +222,7 @@
[tableContentView performSelectorOnMainThread:@selector(displayIfNeeded) withObject:nil waitUntilDone:NO];
// Init copyTable with necessary information for copying selected rows as SQL INSERT
- [tableContentView setTableInstance:self withTableData:tableValues withColumns:dataColumns withTableName:selectedTable withConnection:mySQLConnection];
+ [tableContentView setTableInstance:self withColumns:dataColumns withTableName:selectedTable withConnection:mySQLConnection];
// Post the notification that the query is finished
[[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
@@ -523,12 +524,12 @@
*/
- (void) clearTableValues
{
- NSMutableArray *tableValuesTransition;
+ SPDataStorage *tableValuesTransition;
tableValuesTransition = tableValues;
pthread_mutex_lock(&tableValuesLock);
tableRowsCount = 0;
- tableValues = [[NSMutableArray alloc] init];
+ tableValues = [[SPDataStorage alloc] init];
pthread_mutex_unlock(&tableValuesLock);
[tableValuesTransition release];
}
@@ -604,9 +605,11 @@
// Perform and process the query
[tableContentView performSelectorOnMainThread:@selector(noteNumberOfRowsChanged) withObject:nil waitUntilDone:YES];
[self setUsedQuery:queryString];
+ NSDate *startDate = [NSDate date];
streamingResult = [mySQLConnection streamingQueryString:queryString];
[self processResultIntoDataStorage:streamingResult approximateRowCount:rowsToLoad];
[streamingResult release];
+ NSLog(@"New took %f sec", [[NSDate date] timeIntervalSinceDate:startDate]);
// If the result is empty, and a late page is selected, reset the page
if ([prefs boolForKey:SPLimitResults] && queryStringBeforeLimit && !tableRowsCount && ![mySQLConnection queryCancelled]) {
@@ -654,10 +657,12 @@
- (void)processResultIntoDataStorage:(MCPStreamingResult *)theResult approximateRowCount:(NSUInteger)targetRowCount
{
NSArray *tempRow;
- NSMutableArray *newRow;
- NSMutableArray *columnBlobStatuses = [[NSMutableArray alloc] init];
NSUInteger i;
NSUInteger dataColumnsCount = [dataColumns count];
+ BOOL *columnBlobStatuses = malloc(dataColumnsCount * sizeof(BOOL));;
+
+ // Set the column count on the data store
+ [tableValues setColumnCount:dataColumnsCount];
CGFloat relativeTargetRowCount = 100.0/targetRowCount;
NSUInteger nextTableDisplayBoundary = 50;
@@ -671,7 +676,7 @@
// Build up an array of which columns are blobs for faster iteration
for ( i = 0; i < dataColumnsCount ; i++ ) {
- [columnBlobStatuses addObject:[NSNumber numberWithBool:[tableDataInstance columnIsBlobOrText:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"] ]]];
+ columnBlobStatuses[i] = [tableDataInstance columnIsBlobOrText:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"]];
}
// Set up an autorelease pool for row processing
@@ -682,17 +687,16 @@
pthread_mutex_lock(&tableValuesLock);
if (rowsProcessed < previousTableRowsCount) {
- NSMutableArrayReplaceObject(tableValues, rowsProcessed, [NSMutableArray arrayWithArray:tempRow]);
+ SPDataStorageReplaceRow(tableValues, rowsProcessed, tempRow);
} else {
- NSMutableArrayAddObject(tableValues, [NSMutableArray arrayWithArray:tempRow]);
+ SPDataStorageAddRow(tableValues, tempRow);
}
// Alter the values for hidden blob and text fields if appropriate
if ( prefsLoadBlobsAsNeeded ) {
- newRow = NSArrayObjectAtIndex(tableValues, rowsProcessed);
for ( i = 0 ; i < dataColumnsCount ; i++ ) {
- if ( [NSArrayObjectAtIndex(columnBlobStatuses, i) boolValue] ) {
- NSMutableArrayReplaceObject(newRow, i, [SPNotLoaded notLoaded]);
+ if (columnBlobStatuses[i]) {
+ SPDataStorageReplaceObjectAtRowAndColumn(tableValues, rowsProcessed, i, [SPNotLoaded notLoaded]);
}
}
}
@@ -732,7 +736,7 @@
// If the reloaded table is shorter than the previous table, remove the extra values from the storage
if (tableRowsCount < [tableValues count]) {
pthread_mutex_lock(&tableValuesLock);
- [tableValues removeObjectsInRange:NSMakeRange(tableRowsCount, [tableValues count] - tableRowsCount)];
+ [tableValues removeRowsInRange:NSMakeRange(tableRowsCount, [tableValues count] - tableRowsCount)];
pthread_mutex_unlock(&tableValuesLock);
}
@@ -743,8 +747,8 @@
// Clean up the autorelease pool and reset the progress indicator
[dataLoadingPool drain];
[dataLoadingIndicator setIndeterminate:YES];
-
- [columnBlobStatuses release];
+
+ free(columnBlobStatuses);
}
/**
@@ -1243,7 +1247,7 @@
[newRow addObject:[column objectForKey:@"default"]];
}
}
- [tableValues addObject:newRow];
+ [tableValues addRowWithContents:newRow];
tableRowsCount++;
[tableContentView reloadData];
@@ -1278,8 +1282,8 @@
}
//copy row
- tempRow = [NSMutableArray arrayWithArray:[tableValues objectAtIndex:[tableContentView selectedRow]]];
- [tableValues insertObject:tempRow atIndex:[tableContentView selectedRow]+1];
+ tempRow = [tableValues rowContentsAtIndex:[tableContentView selectedRow]];
+ [tableValues insertRowContents:tempRow atIndex:[tableContentView selectedRow]+1];
tableRowsCount++;
//if we don't show blobs, read data for this duplicate column from db
@@ -1396,7 +1400,7 @@
[tempRow removeAllObjects];
enumerator = [tableColumns objectEnumerator];
while ( (tableColumn = [enumerator nextObject]) ) {
- id o = [NSArrayObjectAtIndex(tableValues, i) objectAtIndex:[[tableColumn identifier] integerValue]];
+ id o = SPDataStorageObjectAtRowAndColumn(tableValues, i, [[tableColumn identifier] integerValue]);
if([o isNSNull])
[tempRow addObject:@"NULL"];
else if ([o isSPNotLoaded])
@@ -1496,7 +1500,7 @@
// Save existing scroll position and details
[spHistoryControllerInstance updateHistoryEntries];
- NSString *targetFilterValue = [[tableValues objectAtIndex:[theArrowCell getClickedRow]] objectAtIndex:dataColumnIndex];
+ NSString *targetFilterValue = [tableValues cellDataAtRow:[theArrowCell getClickedRow] column:dataColumnIndex];
// If the link is within the current table, apply filter settings manually
if ([[refDictionary objectForKey:@"table"] isEqualToString:selectedTable]) {
@@ -1697,7 +1701,7 @@
[[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance];
// If editing, compare the new row to the old row and if they are identical finish editing without saving.
- if (!isEditingNewRow && [oldRow isEqualToArray:[tableValues objectAtIndex:currentlyEditingRow]]) {
+ if (!isEditingNewRow && [oldRow isEqualToArray:[tableValues rowContentsAtIndex:currentlyEditingRow]]) {
isEditingRow = NO;
currentlyEditingRow = -1;
[[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
@@ -1705,9 +1709,10 @@
}
NSMutableArray *fieldValues = [[NSMutableArray alloc] init];
+
// Get the field values
for ( i = 0 ; i < [dataColumns count] ; i++ ) {
- rowObject = [NSArrayObjectAtIndex(tableValues, currentlyEditingRow) objectAtIndex:i];
+ rowObject = [tableValues cellDataAtRow:currentlyEditingRow column:i];
// Add not loaded placeholders directly for easy comparison when added
if (prefsLoadBlobsAsNeeded && !isEditingNewRow && [rowObject isSPNotLoaded])
@@ -1809,7 +1814,7 @@
} else {
NSBeep();
}
- [tableValues replaceObjectAtIndex:currentlyEditingRow withObject:[NSMutableArray arrayWithArray:oldRow]];
+ [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow];
isEditingRow = NO;
isEditingNewRow = NO;
currentlyEditingRow = -1;
@@ -1831,7 +1836,7 @@
// Set the insertId for fields with auto_increment
for ( i = 0; i < [dataColumns count] ; i++ ) {
if ([[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"autoincrement"] integerValue]) {
- [[tableValues objectAtIndex:currentlyEditingRow] replaceObjectAtIndex:i withObject:[[NSNumber numberWithLong:[mySQLConnection insertId]] description]];
+ [tableValues replaceObjectInRow:currentlyEditingRow column:i withObject:[[NSNumber numberWithLong:[mySQLConnection insertId]] description]];
}
}
}
@@ -1948,7 +1953,7 @@
// Use the selected row if appropriate
if ( row >= 0 ) {
- tempValue = [NSArrayObjectAtIndex(tableValues, row) objectAtIndex:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(keys, i)] objectForKey:@"datacolumnindex"] integerValue]];
+ tempValue = [tableValues cellDataAtRow:row column:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(keys, i)] objectForKey:@"datacolumnindex"] integerValue]];
// Otherwise use the oldRow
} else {
@@ -2032,7 +2037,6 @@
*/
{
- NSMutableArray *tempResult = [NSMutableArray array];
NSMutableIndexSet *selectedRows = [NSMutableIndexSet indexSet];
NSString *wherePart;
NSInteger i, errors;
@@ -2051,12 +2055,11 @@
[tableContentView performSelector:@selector(keyDown:) withObject:[NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[tableWindow windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x24] afterDelay:0.0];
} else {
if ( !isEditingNewRow ) {
- [tableValues replaceObjectAtIndex:[tableContentView selectedRow]
- withObject:[NSMutableArray arrayWithArray:oldRow]];
+ [tableValues replaceRowAtIndex:[tableContentView selectedRow] withRowContents:oldRow];
isEditingRow = NO;
} else {
tableRowsCount--;
- [tableValues removeObjectAtIndex:[tableContentView selectedRow]];
+ [tableValues removeRowAtIndex:[tableContentView selectedRow]];
isEditingRow = NO;
isEditingNewRow = NO;
}
@@ -2154,7 +2157,7 @@
while (index != NSNotFound) {
- id keyValue = [NSArrayObjectAtIndex(tableValues, index) objectAtIndex:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(primaryKeyFieldNames,0)] objectForKey:@"datacolumnindex"] integerValue]];
+ id keyValue = [tableValues cellDataAtRow:index column:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(primaryKeyFieldNames,0)] objectForKey:@"datacolumnindex"] integerValue]];
if([keyValue isKindOfClass:[NSData class]])
[deleteQuery appendString:[NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:keyValue]]];
@@ -2199,9 +2202,8 @@
// Build the AND clause of PRIMARY KEYS
[deleteQuery appendString:@"("];
for(NSString *primaryKeyFieldName in primaryKeyFieldNames) {
-
-
- id keyValue = [NSArrayObjectAtIndex(tableValues, index) objectAtIndex:[[[tableDataInstance columnWithName:primaryKeyFieldName] objectForKey:@"datacolumnindex"] integerValue]];
+
+ id keyValue = [tableValues cellDataAtRow:index column:[[[tableDataInstance columnWithName:primaryKeyFieldName] objectForKey:@"datacolumnindex"] integerValue]];
[deleteQuery appendString:[primaryKeyFieldName backtickQuotedString]];
if ([keyValue isKindOfClass:[NSData class]]) {
@@ -2273,12 +2275,10 @@
previousTableRowsCount = tableRowsCount;
[self loadTableValues];
} else {
- for ( i = 0 ; i < tableRowsCount ; i++ ) {
- if ( ![selectedRows containsIndex:i] )
- [tempResult addObject:NSArrayObjectAtIndex(tableValues, i)];
+ for ( i = tableRowsCount - 1 ; i >= 0 ; i-- ) {
+ if ([selectedRows containsIndex:i]) [tableValues removeRowAtIndex:i];
}
- tableRowsCount = [tempResult count];
- [tableValues setArray:tempResult];
+ tableRowsCount = [tableValues count];
[tableContentView reloadData];
}
[tableContentView deselectAll:self];
@@ -2576,7 +2576,7 @@
// possible exceptions (eg for reloading tables etc.)
id theValue;
@try{
- theValue = NSArrayObjectAtIndex(NSArrayObjectAtIndex(tableValues, row), [[aTableColumn identifier] integerValue]);
+ theValue = [tableValues cellDataAtRow:row column:[[aTableColumn identifier] integerValue]];
}
@catch(id ae) {
return nil;
@@ -2613,17 +2613,14 @@
// cases.
if (isWorking) {
pthread_mutex_lock(&tableValuesLock);
- if (rowIndex < tableRowsCount) {
- NSMutableArray *rowData = NSArrayObjectAtIndex(tableValues, rowIndex);
- if (columnIndex < [rowData count]) {
- theValue = NSArrayObjectAtIndex(rowData, columnIndex);
- }
+ if (rowIndex < tableRowsCount && columnIndex < [tableValues columnCount]) {
+ theValue = SPDataStorageObjectAtRowAndColumn(tableValues, rowIndex, columnIndex);
}
pthread_mutex_unlock(&tableValuesLock);
if (!theValue) return @"...";
} else {
- theValue = NSArrayObjectAtIndex(NSArrayObjectAtIndex(tableValues, rowIndex), columnIndex);
+ theValue = SPDataStorageObjectAtRowAndColumn(tableValues, rowIndex, columnIndex);
}
if ([theValue isNSNull])
@@ -2653,11 +2650,8 @@
// rows or columns may be requested. Use gray to indicate loading in these cases.
if (isWorking) {
pthread_mutex_lock(&tableValuesLock);
- if (rowIndex < tableRowsCount) {
- NSMutableArray *rowData = NSArrayObjectAtIndex(tableValues, rowIndex);
- if (columnIndex < [rowData count]) {
- theValue = NSArrayObjectAtIndex(rowData, columnIndex);
- }
+ if (rowIndex < tableRowsCount && columnIndex < [tableValues columnCount]) {
+ theValue = SPDataStorageObjectAtRowAndColumn(tableValues, rowIndex, columnIndex);
}
pthread_mutex_unlock(&tableValuesLock);
@@ -2666,12 +2660,12 @@
return;
}
} else {
- theValue = NSArrayObjectAtIndex(NSArrayObjectAtIndex(tableValues, rowIndex), columnIndex);
+ theValue = SPDataStorageObjectAtRowAndColumn(tableValues, rowIndex, columnIndex);
}
// If user wants to edit 'cell' set text color to black and return to avoid
// writing in gray if value was NULL
- if ( [aTableView editedColumn] == [[aTableColumn identifier] integerValue] && [aTableView editedRow] == rowIndex) {
+ if ( [aTableView editedColumn] == columnIndex && [aTableView editedRow] == rowIndex) {
[cell setTextColor:[NSColor blackColor]];
return;
}
@@ -2691,7 +2685,7 @@
// Catch editing events in the row and if the row isn't currently being edited,
// start an edit. This allows edits including enum changes to save correctly.
if ( !isEditingRow ) {
- [oldRow setArray:NSArrayObjectAtIndex(tableValues, rowIndex)];
+ [oldRow setArray:[tableValues rowContentsAtIndex:rowIndex]];
isEditingRow = YES;
currentlyEditingRow = rowIndex;
}
@@ -2704,9 +2698,9 @@
if ([anObject isEqualToString:[prefs objectForKey:SPNullValue]] && [[column objectForKey:@"null"] boolValue])
anObject = [NSNull null];
- [NSArrayObjectAtIndex(tableValues, rowIndex) replaceObjectAtIndex:[[aTableColumn identifier] integerValue] withObject:anObject];
+ [tableValues replaceObjectInRow:rowIndex column:[[aTableColumn identifier] integerValue] withObject:anObject];
} else {
- [NSArrayObjectAtIndex(tableValues, rowIndex) replaceObjectAtIndex:[[aTableColumn identifier] integerValue] withObject:@""];
+ [tableValues replaceObjectInRow:rowIndex column:[[aTableColumn identifier] integerValue] withObject:@""];
}
}
@@ -2855,7 +2849,7 @@
if ([wherePart length] == 0) return NO;
// If the selected cell hasn't been loaded, load it.
- if ([NSArrayObjectAtIndex(NSArrayObjectAtIndex(tableValues, rowIndex), [[aTableColumn identifier] integerValue]) isSPNotLoaded]) {
+ if ([[tableValues cellDataAtRow:rowIndex column:[[aTableColumn identifier] integerValue]] isSPNotLoaded]) {
// Only get the data for the selected column, not all of them
NSString *query = [NSString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@", [[[aTableColumn headerCell] stringValue] backtickQuotedString], [selectedTable backtickQuotedString], wherePart];
@@ -2868,7 +2862,7 @@
}
NSArray *tempRow = [tempResult fetchRowAsArray];
- [[tableValues objectAtIndex:rowIndex] replaceObjectAtIndex:[[tableContentView tableColumns] indexOfObject:aTableColumn] withObject:[tempRow objectAtIndex:0]];
+ [tableValues replaceObjectInRow:rowIndex column:[[tableContentView tableColumns] indexOfObject:aTableColumn] withObject:[tempRow objectAtIndex:0]];
[tableContentView reloadData];
}
@@ -2881,7 +2875,7 @@
[fieldEditor setTextMaxLength:[[[aTableColumn dataCellForRow:rowIndex] formatter] textLimit]];
- id cellValue = [[tableValues objectAtIndex:rowIndex] objectAtIndex:[[aTableColumn identifier] integerValue]];
+ id cellValue = [tableValues cellDataAtRow:rowIndex column:[[aTableColumn identifier] integerValue]];
if ([cellValue isNSNull]) cellValue = [NSString stringWithString:[prefs objectForKey:SPNullValue]];
id editData = [[fieldEditor editWithObject:cellValue
@@ -2893,7 +2887,7 @@
if (editData) {
if (!isEditingRow) {
- [oldRow setArray:[tableValues objectAtIndex:rowIndex]];
+ [oldRow setArray:[tableValues rowContentsAtIndex:rowIndex]];
isEditingRow = YES;
currentlyEditingRow = rowIndex;
}
@@ -2905,7 +2899,7 @@
[editData release];
editData = [[NSNull null] retain];
}
- [[tableValues objectAtIndex:rowIndex] replaceObjectAtIndex:[[aTableColumn identifier] integerValue] withObject:[[editData copy] autorelease]];
+ [tableValues replaceObjectInRow:rowIndex column:[[aTableColumn identifier] integerValue] withObject:[[editData copy] autorelease]];
}
[fieldEditor release];
@@ -3079,12 +3073,12 @@
[control abortEditing];
if ( isEditingRow && !isEditingNewRow ) {
isEditingRow = NO;
- [tableValues replaceObjectAtIndex:row withObject:[NSMutableArray arrayWithArray:oldRow]];
+ [tableValues replaceRowAtIndex:row withRowContents:oldRow];
} else if ( isEditingNewRow ) {
isEditingRow = NO;
isEditingNewRow = NO;
tableRowsCount--;
- [tableValues removeObjectAtIndex:row];
+ [tableValues removeRowAtIndex:row];
[tableContentView reloadData];
}
currentlyEditingRow = -1;
diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj
index d76b6728..ebedb972 100644
--- a/sequel-pro.xcodeproj/project.pbxproj
+++ b/sequel-pro.xcodeproj/project.pbxproj
@@ -138,6 +138,7 @@
584F5F8F0F50ACD800036517 /* table-view-small.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584F5F8E0F50ACD800036517 /* table-view-small.tiff */; };
586F457B0FDB269E00B428D7 /* RegexKitLite.m in Sources */ = {isa = PBXBuildFile; fileRef = 296DC8AB0F909194002A3258 /* RegexKitLite.m */; };
586F457E0FDB280100B428D7 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 296DC8BE0F9091DF002A3258 /* libicucore.dylib */; };
+ 5870868410FA3E9C00D58E1C /* SPDataStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 5870868310FA3E9C00D58E1C /* SPDataStorage.m */; };
5885940F0F7AEE6000ED0E67 /* sparkle-public-key.pem in Resources */ = {isa = PBXBuildFile; fileRef = 5885940E0F7AEE6000ED0E67 /* sparkle-public-key.pem */; };
588B2CC80FE5641E00EC5FC0 /* ssh-connected.png in Resources */ = {isa = PBXBuildFile; fileRef = 588B2CC50FE5641E00EC5FC0 /* ssh-connected.png */; };
588B2CC90FE5641E00EC5FC0 /* ssh-connecting.png in Resources */ = {isa = PBXBuildFile; fileRef = 588B2CC60FE5641E00EC5FC0 /* ssh-connecting.png */; };
@@ -520,6 +521,8 @@
584192A0101E57BB0089807F /* NSMutableArray-MultipleSort.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray-MultipleSort.m"; sourceTree = "<group>"; };
584F5F8E0F50ACD800036517 /* table-view-small.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "table-view-small.tiff"; sourceTree = "<group>"; };
586F432A0FD74CFC00B428D7 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/SSHQuestionDialog.xib; sourceTree = "<group>"; };
+ 5870868210FA3E9C00D58E1C /* SPDataStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPDataStorage.h; sourceTree = "<group>"; };
+ 5870868310FA3E9C00D58E1C /* SPDataStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDataStorage.m; sourceTree = "<group>"; };
588593F30F7AEC9500ED0E67 /* package-application.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "package-application.sh"; sourceTree = "<group>"; };
5885940E0F7AEE6000ED0E67 /* sparkle-public-key.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "sparkle-public-key.pem"; sourceTree = "<group>"; };
588B2CC50FE5641E00EC5FC0 /* ssh-connected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ssh-connected.png"; sourceTree = "<group>"; };
@@ -1396,6 +1399,8 @@
BC2C16D30FEBEDF10003993B /* SPDataAdditions.m */,
582A01E7107C0C170027D42B /* SPNotLoaded.h */,
582A01E8107C0C170027D42B /* SPNotLoaded.m */,
+ 5870868210FA3E9C00D58E1C /* SPDataStorage.h */,
+ 5870868310FA3E9C00D58E1C /* SPDataStorage.m */,
);
name = "Category Additions";
sourceTree = "<group>";
@@ -1784,6 +1789,7 @@
1792C26110AE1A2D00ABE758 /* SPConnectionDelegate.m in Sources */,
17CC97F310B4ABE90034CD7A /* SPAboutController.m in Sources */,
58C34F5310B86CAE00D37E14 /* NSNotificationAdditions.m in Sources */,
+ 5870868410FA3E9C00D58E1C /* SPDataStorage.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};