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:
rfm 2015-04-28 11:47:23 +00:00
parent 6d94e489ff
commit 67bc2571e7
5 changed files with 227 additions and 117 deletions

View file

@ -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.

View file

@ -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];
}

View file

@ -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;

View file

@ -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;

View file

@ -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);
}
}