From bb8284b4fe7eab611f287d11a1cacc9125a4efcd Mon Sep 17 00:00:00 2001 From: rfm Date: Wed, 1 Apr 2015 11:32:49 +0000 Subject: [PATCH] Threading/notification improvments git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/sqlclient/trunk@38447 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 21 +++++++++++++++++++-- Postgres.m | 22 ++++++++++++++-------- SQLClient.h | 34 +++++++++++++++++++++++++++------- SQLClient.m | 14 ++++++++++++++ testPostgres.m | 2 +- 5 files changed, 75 insertions(+), 18 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4e7c546..30be4b8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,11 +1,28 @@ +2015-04-01 Richard Frith-Macdonald + + * SQLClient.h: + * Postgres.m: + Fixup notification posting to be asynchronous using the default + notification queue for the thread so that the notifications do + not get delivered while the query/statement at which they were + detected is still in progress. + Add method to explicitly grab/release the client for the current + thread. + 2015-03-11 Richard Frith-Macdonald - * SQLClientPool.m: Fixup for ewxposing prepare method + + * SQLClientPool.m: Fixup for exposing prepare method * SQLClient.h: * SQLClient.m: * Postgres.m: * testPostgres.m: Add simple array support for char/varchar/text, integer/real, - timestamp, bool and bytea. + timestamp, bool and bytea. When a query returns an array of + one of these types, the resulting object is an NSArray containing + the database array elements rather than an NSString containing the + string literal representation of the database array. + Also added a method to convert an NSArray to a string literal + representation of a database array. 2015-03-02 Richard Frith-Macdonald diff --git a/Postgres.m b/Postgres.m index 44efdb5..f4df7a5 100644 --- a/Postgres.m +++ b/Postgres.m @@ -35,6 +35,7 @@ #import #import #import +#import #import #import #import @@ -279,8 +280,8 @@ connectQuote(NSString *str) - (void) _checkNotifications { - static NSNotificationCenter *nc; - PGnotify *notify; + NSNotificationQueue *nq = nil; + PGnotify *notify; while ((notify = PQnotifies(connection)) != 0) { @@ -328,11 +329,14 @@ connectQuote(NSString *str) userInfo: (NSDictionary*)userInfo]; [name release]; [userInfo release]; - if (nil == nc) + + if (nil == nq) { - nc = [[NSNotificationCenter defaultCenter] retain]; + nq = [NSNotificationQueue defaultQueue]; } - [nc postNotification: n]; + /* Post asynchronously + */ + [nq enqueueNotification: n postingStyle: NSPostASAP]; } NS_HANDLER { @@ -523,9 +527,9 @@ static unsigned int trim(char *str) } if ('\"' == *p) { - *p++ = '\0'; + *p = '\0'; } - if (len == (p - start + 1)) + if (len == (p - start)) { v = [[NSString alloc] initWithUTF8String: start]; } @@ -562,6 +566,7 @@ static unsigned int trim(char *str) freeWhenDone: YES]; } } + *p++ = '\"'; } else { @@ -677,6 +682,7 @@ static unsigned int trim(char *str) case 1002: // CHAR ARRAY case 1009: // TEXT ARRAY case 1015: // VARCHAR ARRAY + case 1263: // CSTRING ARRAY if ('{' == *p) { NSMutableArray *a; @@ -1212,7 +1218,7 @@ static unsigned int trim(char *str) else if ([o isKindOfClass: [NSDate class]]) { [s appendString: [self quote: (NSString*)o]]; - [s appendString: @"::timestamp"]; + [s appendString: @"::timestamp with time zone"]; } else if ([o isKindOfClass: [NSData class]]) { diff --git a/SQLClient.h b/SQLClient.h index f6f9b4c..43ad988 100644 --- a/SQLClient.h +++ b/SQLClient.h @@ -488,6 +488,13 @@ SQLCLIENT_PRIVATE */ - (void) begin; +/** This grabs the receiver for use by the current thread.
+ * If limit is nil or in the past, makes a single immediate attempt.
+ * Returns NO if it fails to obtain a lock by the specified date.
+ * Must be matched by an -unlock if it succeeds. + */ +- (BOOL) lockBeforeDate: (NSDate*)limit; + /** *

Build an sql query string using the supplied arguments. *

@@ -787,7 +794,12 @@ SQLCLIENT_PRIVATE * YYYY-MM-DD hh:mm:ss.mmm ?ZZZZ
* NSData objects are not quoted ... they must not appear in queries, and * where used for insert/update operations, they need to be passed to the - * -backendExecute: method unchanged. + * -backendExecute: method unchanged.
+ * NSArray and NSSet objects are quoted as sets containing the quoted + * elements from the array/set. If you want to use SQL arrays (and your + * database backend supports it) you must explicitly use the + * -quoteArray:toString:quotingString: to convert an NSArray to a literal + * database array representation. */ - (NSString*) quote: (id)obj; @@ -928,6 +940,10 @@ SQLCLIENT_PRIVATE recordType: (id)rtype listType: (id)ltype; +/** Releases a lock previously obtained using -lockbeforeDate: + */ +- (void) unlock; + /** * Return the database user for this instance (or nil). */ @@ -1088,7 +1104,8 @@ SQLCLIENT_PRIVATE * which don't support asynchronous notifications.
* If a backend does support asynchronous notifications, * it should do so by posting NSNotification instances to - * [NSNotificationCenter defaultCenter] using the SQLClient instance as + * [NSNotificationQueue defaultQueue] with the posting style NSPostASAP + * (to post asynchronously) and using the SQLClient instance as * the notification object and supplying any payload as a string using * the 'Payload' key in the NSNotification userInfo dictionary. * The userInfo dictionary should also contain a boolean (NSNumber) value, @@ -1182,11 +1199,14 @@ SQLCLIENT_PRIVATE * as an action by this SQLClient instance.
* If the 'Payload' value is not nil, then it is a string providing extra * information about the notification.
- * NB. At the point when the observer is notified about an event the - * database client object will be locked and may not be used to query - * or modify the database (typically a database query will already be - * in progress). The method handling the notification must therefore - * handle any database operations in a later timeout. + * Notifications are posted asynchronously using the default notification + * queue for the current thread, so they should be delivered to the + * observer after the database statement in which they were detected + * has completed. However, delivery of the notification could still + * occur inside a transaction is the -begin and -commit statements + * are used. For this reason, observing code may want to use the + * -lockBeforeDate: -isInTransaction and -unlock methods to ensure + * that they don't interfere with ongoing transactions. */ - (void) addObserver: (id)anObserver selector: (SEL)aSelector diff --git a/SQLClient.m b/SQLClient.m index df1f937..9f3ae5c 100644 --- a/SQLClient.m +++ b/SQLClient.m @@ -1341,6 +1341,15 @@ static int poolConnections = 0; return nil; } +- (BOOL) lockBeforeDate: (NSDate*)limit +{ + if (nil == limit) + { + return [lock tryLock]; + } + return [lock lockBeforeDate: limit]; +} + - (SQLClient*) longestIdle: (SQLClient*)other { NSTimeInterval t0; @@ -2015,6 +2024,11 @@ static int poolConnections = 0; return result; } +- (void) unlock +{ + [lock unlock]; +} + - (NSString*) user { return _user; diff --git a/testPostgres.m b/testPostgres.m index 6da17f8..fed637e 100644 --- a/testPostgres.m +++ b/testPostgres.m @@ -260,7 +260,7 @@ main() @"extra2 varchar[]," @"extra3 bytea[]," @"extra4 boolean[]," - @"extra5 timestamp[]" + @"extra5 timestamp with time zone[]" @")", nil];