mirror of
https://github.com/gnustep/libs-sqlclient.git
synced 2025-02-20 18:32:06 +00:00
performance tweaks
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/sqlclient/trunk@38461 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
6d94e489ff
commit
67bc2571e7
5 changed files with 227 additions and 117 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
|||
2015-04-28 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* SQLClient.h:
|
||||
* SQLClient.m:
|
||||
* Postgres.m:
|
||||
Deprecate trtansaction merging.
|
||||
Rewrite SQLRecord concrete class to use a new SQLRecordKeys object
|
||||
shared between all the records produced by a query (as a performance
|
||||
enhancement for large queries).
|
||||
|
||||
2015-04-15 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Postgres.m: notifications are posted in main thread.
|
||||
|
|
23
Postgres.m
23
Postgres.m
|
@ -772,6 +772,7 @@ static unsigned int trim(char *str)
|
|||
int ftype[fieldCount];
|
||||
int fmod[fieldCount];
|
||||
int fformat[fieldCount];
|
||||
SQLRecordKeys *k = nil;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < fieldCount; i++)
|
||||
|
@ -816,9 +817,25 @@ static unsigned int trim(char *str)
|
|||
}
|
||||
values[j] = v;
|
||||
}
|
||||
record = [rtype newWithValues: values
|
||||
keys: keys
|
||||
count: fieldCount];
|
||||
if (nil == k)
|
||||
{
|
||||
/* We don't have keys information, so use the
|
||||
* constructor where we list keys and, if the
|
||||
* resulting record provides keys information
|
||||
* on the first record, we save it for later.
|
||||
*/
|
||||
record = [rtype newWithValues: values
|
||||
keys: keys
|
||||
count: fieldCount];
|
||||
if (0 == i)
|
||||
{
|
||||
k = [record keys];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
record = [rtype newWithValues: values keys: k];
|
||||
}
|
||||
[records addObject: record];
|
||||
[record release];
|
||||
}
|
||||
|
|
80
SQLClient.h
80
SQLClient.h
|
@ -208,6 +208,36 @@ extern NSString * const SQLClientDidDisconnectNotification;
|
|||
#define SQLCLIENT_PRIVATE @private
|
||||
#endif
|
||||
|
||||
/** This class is used to hold key information for a set of SQLRecord
|
||||
* objects produced by a single query.
|
||||
*/
|
||||
@interface SQLRecordKeys : NSObject
|
||||
{
|
||||
NSUInteger count; // Number of keys
|
||||
NSArray *order; // Keys in order
|
||||
NSMapTable *map; // Key to index
|
||||
NSMapTable *low; // lowercase map
|
||||
}
|
||||
|
||||
/** Returns the number of keys in the receiver.
|
||||
*/
|
||||
- (NSUInteger) count;
|
||||
|
||||
/** Returns the index of the object with the specified key,
|
||||
* or NSNotFound if there is no such key.
|
||||
*/
|
||||
- (NSUInteger) indexForKey: (NSString*)key;
|
||||
|
||||
/** Initialiser
|
||||
*/
|
||||
- (id) initWithKeys: (NSString**)keys count: (NSUInteger)c;
|
||||
|
||||
/** Returns an array containing the record field names in order.
|
||||
*/
|
||||
- (NSArray*) order;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* <p>An enhanced array to represent a record returned from a query.
|
||||
* You should <em>NOT</em> try to create instances of this class
|
||||
|
@ -238,6 +268,17 @@ extern NSString * const SQLClientDidDisconnectNotification;
|
|||
keys: (NSString**)k
|
||||
count: (unsigned int)c;
|
||||
|
||||
/**
|
||||
* Create a new SQLRecord containing the specified fields.<br />
|
||||
* NB. The values and keys are <em>retained</em> by the record rather
|
||||
* than being copied.<br />
|
||||
* A nil value is represented by [NSNull null].<br />
|
||||
* This constructor will be used for subsequent records after the first
|
||||
* in a query iff the first record created returns a non-nil result when
|
||||
* sent the -keys method.
|
||||
*/
|
||||
+ (id) newWithValues: (id*)v keys: (SQLRecordKeys*)k;
|
||||
|
||||
/**
|
||||
* Returns an array containing the names of all the fields in the record.
|
||||
*/
|
||||
|
@ -266,10 +307,16 @@ extern NSString * const SQLClientDidDisconnectNotification;
|
|||
- (void) getObjects: (id*)buf;
|
||||
|
||||
/** <override-subclass />
|
||||
* Returns the key at the specified indes.<br />
|
||||
* Returns the key at the specified index.<br />
|
||||
*/
|
||||
- (NSString*) keyAtIndex: (NSUInteger)index;
|
||||
|
||||
/** Returns the keys used by this record.
|
||||
* The abstract class returns nil, so subclasses should override if
|
||||
* they wish to make use of the +newWithValues:keys: method.
|
||||
*/
|
||||
- (SQLRecordKeys*) keys;
|
||||
|
||||
/** <override-subclass />
|
||||
* Returns the object at the specified indes.<br />
|
||||
*/
|
||||
|
@ -1791,35 +1838,10 @@ SQLCLIENT_PRIVATE
|
|||
*/
|
||||
- (void) reset;
|
||||
|
||||
/** <p>Use this method to enable merging of statemements subsequently added
|
||||
* or appended to the receiver. The history argument specifies how many
|
||||
* of the most recent statements in the transaction should be checked for
|
||||
* merging in a new statement, with a value of zero meaning that no
|
||||
* merging is done.<br />
|
||||
* Returns the previous setting for the transaction.
|
||||
/** <p>DEPRECATED ... merging of statments is quite database specific and
|
||||
* also much better done by hand rather than trying to rely on anything
|
||||
* automatic.
|
||||
* </p>
|
||||
* <p>You may use this feature with an insert statement of the form:<br />
|
||||
* INSERT INTO table (fieldnames) VALUES (values);<br />
|
||||
* For databases which support multiline inserts such that they can be
|
||||
* merged into something of the form:
|
||||
* INSERT INTO table (fieldnames) VALUES (values1),(values2),...;
|
||||
* </p>
|
||||
* <p>Or may use this with an update or delete statement of the form:<br />
|
||||
* command table SET settings WHERE condition;<br />
|
||||
* So that statements may be merged into:<br />
|
||||
* command table SET settings WHERE (condition1) OR (condition2) OR ...;
|
||||
* </p>
|
||||
* If no opportunity for merging is found, the new statement is simply
|
||||
* added to the transaction.<br />
|
||||
* Caveats:<br />
|
||||
* 1. databases may not actually support multiline insert.<br />
|
||||
* 2. Merging is done only if the statement up to the string 'VALUES'
|
||||
* (for insert) or 'WHERE' (for update) matches.<br />
|
||||
* 3. Merging into any of the last N statements (where N is greater than 1)
|
||||
* may of course change the order of statements in the transaction,
|
||||
* so care must be taken not to use this feature where that might matter.<br />
|
||||
* 4. This is a simple text match rather than sql syntactic analysis,
|
||||
* so it's possible to confuse the process with complex statements.
|
||||
*/
|
||||
- (uint8_t) setMerge: (uint8_t)history;
|
||||
|
||||
|
|
226
SQLClient.m
226
SQLClient.m
|
@ -73,6 +73,9 @@ NSString * const SQLClientDidConnectNotification
|
|||
NSString * const SQLClientDidDisconnectNotification
|
||||
= @"SQLClientDidDisconnectNotification";
|
||||
|
||||
static unsigned int classDebugging = 0;
|
||||
static NSTimeInterval classDuration = -1;
|
||||
|
||||
static NSNull *null = nil;
|
||||
static NSArray *queryModes = nil;
|
||||
static NSThread *mainThread = nil;
|
||||
|
@ -82,9 +85,76 @@ static Class NSDateClass = Nil;
|
|||
static Class NSSetClass = Nil;
|
||||
static Class SQLClientClass = Nil;
|
||||
|
||||
@implementation SQLRecordKeys
|
||||
|
||||
- (NSUInteger) count
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
if (nil != order) [order release];
|
||||
if (nil != map) [map release];
|
||||
if (nil != low) [low release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSUInteger) indexForKey: (NSString*)key
|
||||
{
|
||||
NSUInteger c;
|
||||
|
||||
c = (NSUInteger)NSMapGet(map, key);
|
||||
if (c > 0)
|
||||
{
|
||||
return c - 1;
|
||||
}
|
||||
key = [key lowercaseString];
|
||||
c = (NSUInteger)NSMapGet(low, key);
|
||||
if (c > 0)
|
||||
{
|
||||
if (classDebugging > 0)
|
||||
{
|
||||
NSLog(@"[SQLRecordKeys-indexForKey:] lowercase '%@'", key);
|
||||
}
|
||||
return c - 1;
|
||||
}
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
- (id) initWithKeys: (NSString**)keys count: (NSUInteger)c
|
||||
{
|
||||
if (nil != (self = [super init]))
|
||||
{
|
||||
count = c;
|
||||
order = [[NSArray alloc] initWithObjects: keys count: c];
|
||||
map = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
||||
NSIntegerMapValueCallBacks, count);
|
||||
low = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
||||
NSIntegerMapValueCallBacks, count);
|
||||
for (c = 1; c <= count; c++)
|
||||
{
|
||||
NSString *k = keys[c-1];
|
||||
|
||||
NSMapInsert(map, (void*)k, (void*)c);
|
||||
k = [k lowercaseString];
|
||||
NSMapInsert(low, (void*)k, (void*)c);
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray*) order
|
||||
{
|
||||
return order;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@interface _ConcreteSQLRecord : SQLRecord
|
||||
{
|
||||
unsigned count;
|
||||
SQLRecordKeys *keys;
|
||||
NSUInteger count; // Must be last
|
||||
}
|
||||
@end
|
||||
|
||||
|
@ -132,6 +202,11 @@ static Class rClass = 0;
|
|||
return [rClass newWithValues: v keys: k count: c];
|
||||
}
|
||||
|
||||
+ (id) newWithValues: (id*)v keys: (SQLRecordKeys*)k
|
||||
{
|
||||
return [rClass newWithValues: v keys: k];
|
||||
}
|
||||
|
||||
- (NSArray*) allKeys
|
||||
{
|
||||
NSUInteger count = [self count];
|
||||
|
@ -186,7 +261,7 @@ static Class rClass = 0;
|
|||
|
||||
- (void) getKeys: (id*)buf
|
||||
{
|
||||
unsigned i = [self count];
|
||||
NSUInteger i = [self count];
|
||||
|
||||
while (i-- > 0)
|
||||
{
|
||||
|
@ -196,7 +271,7 @@ static Class rClass = 0;
|
|||
|
||||
- (void) getObjects: (id*)buf
|
||||
{
|
||||
unsigned i = [self count];
|
||||
NSUInteger i = [self count];
|
||||
|
||||
while (i-- > 0)
|
||||
{
|
||||
|
@ -217,6 +292,11 @@ static Class rClass = 0;
|
|||
return nil;
|
||||
}
|
||||
|
||||
- (SQLRecordKeys*) keys
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id) objectAtIndex: (NSUInteger)index
|
||||
{
|
||||
SUBCLASS_RESPONSIBILITY
|
||||
|
@ -354,37 +434,46 @@ static Class rClass = 0;
|
|||
|
||||
@implementation _ConcreteSQLRecord
|
||||
|
||||
+ (id) newWithValues: (id*)v keys: (NSString**)k count: (unsigned int)c
|
||||
+ (id) newWithValues: (id*)v keys: (SQLRecordKeys*)k
|
||||
{
|
||||
id *ptr;
|
||||
id *ptr;
|
||||
_ConcreteSQLRecord *r;
|
||||
unsigned pos;
|
||||
NSUInteger c;
|
||||
|
||||
c = [k count];
|
||||
r = (_ConcreteSQLRecord*)NSAllocateObject(self,
|
||||
c*2*sizeof(id), NSDefaultMallocZone());
|
||||
c*sizeof(id), NSDefaultMallocZone());
|
||||
r->count = c;
|
||||
ptr = ((void*)&(r->count)) + sizeof(r->count);
|
||||
for (pos = 0; pos < c; pos++)
|
||||
r->keys = [k retain];
|
||||
ptr = (id*)(((void*)&(r->count)) + sizeof(r->count));
|
||||
while (c-- > 0)
|
||||
{
|
||||
if (v[pos] == nil)
|
||||
if (nil == v[c])
|
||||
{
|
||||
ptr[pos] = [null retain];
|
||||
ptr[c] = [null retain];
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr[pos] = [v[pos] retain];
|
||||
ptr[c] = [v[c] retain];
|
||||
}
|
||||
ptr[pos + c] = [k[pos] retain];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
+ (id) newWithValues: (id*)v keys: (NSString**)k count: (unsigned int)c
|
||||
{
|
||||
SQLRecordKeys *o;
|
||||
_ConcreteSQLRecord *r;
|
||||
|
||||
o = [[SQLRecordKeys alloc] initWithKeys: k count: c];
|
||||
r = [self newWithValues: v keys: o];
|
||||
[o release];
|
||||
return r;
|
||||
}
|
||||
|
||||
- (NSArray*) allKeys
|
||||
{
|
||||
id *ptr;
|
||||
|
||||
ptr = ((void*)&count) + sizeof(count);
|
||||
return [NSArray arrayWithObjects: &ptr[count] count: count];
|
||||
return [keys order];
|
||||
}
|
||||
|
||||
- (id) copyWithZone: (NSZone*)z
|
||||
|
@ -400,13 +489,13 @@ static Class rClass = 0;
|
|||
- (void) dealloc
|
||||
{
|
||||
id *ptr;
|
||||
unsigned pos;
|
||||
NSUInteger pos;
|
||||
|
||||
ptr = ((void*)&count) + sizeof(count);
|
||||
[keys release];
|
||||
ptr = (id*)(((void*)&count) + sizeof(count));
|
||||
for (pos = 0; pos < count; pos++)
|
||||
{
|
||||
[ptr[pos] release]; ptr[pos] = nil;
|
||||
[ptr[count + pos] release]; ptr[count + pos] = nil;
|
||||
}
|
||||
[super dealloc];
|
||||
}
|
||||
|
@ -414,37 +503,31 @@ static Class rClass = 0;
|
|||
- (NSMutableDictionary*) dictionary
|
||||
{
|
||||
NSMutableDictionary *d;
|
||||
unsigned pos;
|
||||
NSUInteger pos;
|
||||
NSArray *k = [keys order];
|
||||
id *ptr;
|
||||
|
||||
ptr = ((void*)&count) + sizeof(count);
|
||||
ptr = (id*)(((void*)&count) + sizeof(count));
|
||||
d = [NSMutableDictionary dictionaryWithCapacity: count];
|
||||
for (pos = 0; pos < count; pos++)
|
||||
{
|
||||
[d setObject: ptr[pos] forKey: [ptr[pos + count] lowercaseString]];
|
||||
[d setObject: ptr[pos]
|
||||
forKey: [[k objectAtIndex: pos] lowercaseString]];
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
- (void) getKeys: (id*)buf
|
||||
{
|
||||
id *ptr;
|
||||
unsigned pos;
|
||||
|
||||
ptr = ((void*)&count) + sizeof(count);
|
||||
ptr += count; // Step past objects to keys.
|
||||
for (pos = 0; pos < count; pos++)
|
||||
{
|
||||
buf[pos] = ptr[pos];
|
||||
}
|
||||
[[keys order] getObjects: buf];
|
||||
}
|
||||
|
||||
- (void) getObjects: (id*)buf
|
||||
{
|
||||
id *ptr;
|
||||
unsigned pos;
|
||||
NSUInteger pos;
|
||||
|
||||
ptr = ((void*)&count) + sizeof(count);
|
||||
ptr = (id*)(((void*)&count) + sizeof(count));
|
||||
for (pos = 0; pos < count; pos++)
|
||||
{
|
||||
buf[pos] = ptr[pos];
|
||||
|
@ -460,16 +543,12 @@ static Class rClass = 0;
|
|||
|
||||
- (NSString*) keyAtIndex: (NSUInteger)pos
|
||||
{
|
||||
id *ptr;
|
||||
return [[keys order] objectAtIndex: pos];
|
||||
}
|
||||
|
||||
if (pos >= count)
|
||||
{
|
||||
[NSException raise: NSRangeException
|
||||
format: @"Array index too large"];
|
||||
}
|
||||
ptr = ((void*)&count) + sizeof(count);
|
||||
ptr += count;
|
||||
return ptr[pos];
|
||||
- (SQLRecordKeys*) keys
|
||||
{
|
||||
return keys;
|
||||
}
|
||||
|
||||
- (id) objectAtIndex: (NSUInteger)pos
|
||||
|
@ -481,31 +560,25 @@ static Class rClass = 0;
|
|||
[NSException raise: NSRangeException
|
||||
format: @"Array index too large"];
|
||||
}
|
||||
ptr = ((void*)&count) + sizeof(count);
|
||||
ptr = (id*)(((void*)&count) + sizeof(count));
|
||||
return ptr[pos];
|
||||
}
|
||||
|
||||
- (id) objectForKey: (NSString*)key
|
||||
{
|
||||
id *ptr;
|
||||
unsigned int pos;
|
||||
NSUInteger pos = [keys indexForKey: key];
|
||||
|
||||
ptr = ((void*)&count) + sizeof(count);
|
||||
for (pos = 0; pos < count; pos++)
|
||||
if (NSNotFound == pos)
|
||||
{
|
||||
if ([key isEqualToString: ptr[pos + count]] == YES)
|
||||
{
|
||||
return ptr[pos];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
for (pos = 0; pos < count; pos++)
|
||||
else
|
||||
{
|
||||
if ([key caseInsensitiveCompare: ptr[pos + count]] == NSOrderedSame)
|
||||
{
|
||||
return ptr[pos];
|
||||
}
|
||||
id *ptr;
|
||||
|
||||
ptr = (id*)(((void*)&count) + sizeof(count));
|
||||
return ptr[pos];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void) replaceObjectAtIndex: (NSUInteger)index withObject: (id)anObject
|
||||
|
@ -521,7 +594,7 @@ static Class rClass = 0;
|
|||
{
|
||||
anObject = null;
|
||||
}
|
||||
ptr = ((void*)&count) + sizeof(count);
|
||||
ptr = (id*)(((void*)&count) + sizeof(count));
|
||||
ptr += index;
|
||||
[anObject retain];
|
||||
[*ptr release];
|
||||
|
@ -531,35 +604,25 @@ static Class rClass = 0;
|
|||
- (void) setObject: (id)anObject forKey: (NSString*)aKey
|
||||
{
|
||||
id *ptr;
|
||||
unsigned int pos;
|
||||
NSUInteger pos;
|
||||
|
||||
if (anObject == nil)
|
||||
{
|
||||
anObject = null;
|
||||
}
|
||||
ptr = ((void*)&count) + sizeof(count);
|
||||
for (pos = 0; pos < count; pos++)
|
||||
ptr = (id*)(((void*)&count) + sizeof(count));
|
||||
pos = [keys indexForKey: aKey];
|
||||
if (NSNotFound == pos)
|
||||
{
|
||||
if ([aKey isEqualToString: ptr[pos + count]] == YES)
|
||||
{
|
||||
[anObject retain];
|
||||
[ptr[pos] release];
|
||||
ptr[pos] = anObject;
|
||||
return;
|
||||
}
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"Bad key (%@) in -setObject:forKey:", aKey];
|
||||
}
|
||||
for (pos = 0; pos < count; pos++)
|
||||
else
|
||||
{
|
||||
if ([aKey caseInsensitiveCompare: ptr[pos + count]] == NSOrderedSame)
|
||||
{
|
||||
[anObject retain];
|
||||
[ptr[pos] release];
|
||||
ptr[pos] = anObject;
|
||||
return;
|
||||
}
|
||||
[anObject retain];
|
||||
[ptr[pos] release];
|
||||
ptr[pos] = anObject;
|
||||
}
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"Bad key (%@) in -setObject:forKey:", aKey];
|
||||
}
|
||||
|
||||
- (NSUInteger) sizeInBytes: (NSMutableSet*)exclude
|
||||
|
@ -574,7 +637,7 @@ static Class rClass = 0;
|
|||
NSUInteger pos;
|
||||
id *ptr;
|
||||
|
||||
ptr = ((void*)&count) + sizeof(count);
|
||||
ptr = (id*)(((void*)&count) + sizeof(count));
|
||||
for (pos = 0; pos < count; pos++)
|
||||
{
|
||||
size += [ptr[pos] sizeInBytes: exclude];
|
||||
|
@ -605,9 +668,6 @@ NSString *SQLUniqueException = @"SQLUniqueException";
|
|||
|
||||
@implementation SQLClient (Logging)
|
||||
|
||||
static unsigned int classDebugging = 0;
|
||||
static NSTimeInterval classDuration = -1;
|
||||
|
||||
+ (unsigned int) debugging
|
||||
{
|
||||
return classDebugging;
|
||||
|
|
|
@ -367,9 +367,10 @@ main()
|
|||
{
|
||||
NSLog(@"Retrieved non-latin does not match saved string");
|
||||
}
|
||||
if ([[record objectForKey: @"k"] isEqual: oddChars] == NO)
|
||||
id o = [[record objectForKey: @"k"] stringByTrimmingSpaces];
|
||||
if ([o isEqual: oddChars] == NO)
|
||||
{
|
||||
NSLog(@"Retrieved odd chars does not match saved string");
|
||||
NSLog(@"Retrieved odd chars (%@) does not match saved string (%@)", o, oddChars);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue