mirror of
https://github.com/gnustep/libs-sqlclient.git
synced 2025-02-15 16:11:42 +00:00
Post notifications on connect and disconnect.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/sqlclient/trunk@25309 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
3dea330f47
commit
fb09bfd147
4 changed files with 296 additions and 121 deletions
|
@ -1,7 +1,14 @@
|
|||
2007-07-09 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* SQLClient.m: Post notifications upon connect and disconnect.
|
||||
|
||||
2007-07-07 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* SQLClient.m: Fix error causing loss of some debug output when an
|
||||
exception occurs in a transaction.
|
||||
Rewrite transaction code to support execution with automatic retry of
|
||||
statements when batching.
|
||||
* JDBC.m: Update for new transaction code
|
||||
|
||||
2007-04-01 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
|
|
106
JDBC.m
106
JDBC.m
|
@ -1675,64 +1675,39 @@ static int JDBCVARCHAR = 0;
|
|||
|
||||
@implementation _JDBCTransaction
|
||||
|
||||
// Marker for the end of data owned by a statement
|
||||
static id marker = @"End of statement data";
|
||||
|
||||
- (NSString*) description
|
||||
- (BOOL) _batchable: (NSArray*)a
|
||||
{
|
||||
return [NSString stringWithFormat: @"%@ with SQL '%@' for %@",
|
||||
[super description],
|
||||
(_count == 0 ? (id)@"" : (id)[_info objectAtIndex: 0]), _db];
|
||||
unsigned c = [a count];
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < c; i++)
|
||||
{
|
||||
if ([[a objectAtIndex: i] count] > 1)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) _addInfo: (NSArray*)info
|
||||
- (void) _merge: (NSMutableArray*)a
|
||||
{
|
||||
if (_count == 0)
|
||||
{
|
||||
id o = [info objectAtIndex: 0];
|
||||
NSMutableArray *ma;
|
||||
unsigned c = [_info count];
|
||||
unsigned i;
|
||||
|
||||
if ([o isKindOfClass: [NSString class]] == YES)
|
||||
for (i = 0; i < c; i++)
|
||||
{
|
||||
id o = [_info objectAtIndex: i];
|
||||
|
||||
if ([o isKindOfClass: [NSArray class]] == YES)
|
||||
{
|
||||
ma = [[NSMutableArray alloc] initWithObjects: &o count: 1];
|
||||
}
|
||||
[a addObject: o];
|
||||
}
|
||||
else
|
||||
{
|
||||
ma = [(NSArray*)o mutableCopy];
|
||||
}
|
||||
[_info addObjectsFromArray: info];
|
||||
[_info replaceObjectAtIndex: 0 withObject: ma];
|
||||
RELEASE(ma);
|
||||
[(_JDBCTransaction*)o _merge: a];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned c = [info count];
|
||||
unsigned i = 1;
|
||||
id o = [info objectAtIndex: 0];
|
||||
NSMutableArray *ma = [_info objectAtIndex: 0];
|
||||
|
||||
if ([o isKindOfClass: [NSString class]] == YES)
|
||||
{
|
||||
[ma addObject: (NSString*)o];
|
||||
}
|
||||
else
|
||||
{
|
||||
[ma addObjectsFromArray: (NSArray*)o];
|
||||
}
|
||||
while (i < c)
|
||||
{
|
||||
[_info addObject: [info objectAtIndex: i++]];
|
||||
}
|
||||
}
|
||||
|
||||
/* If the info item being added is a simple statement rather than the
|
||||
* content of another transaction, we must add an end-of-statement marker.
|
||||
*/
|
||||
if ([info lastObject] != marker)
|
||||
{
|
||||
[_info addObject: marker];
|
||||
}
|
||||
_count++;
|
||||
}
|
||||
|
||||
- (void) execute
|
||||
|
@ -1768,13 +1743,16 @@ static id marker = @"End of statement data";
|
|||
|
||||
NS_DURING
|
||||
{
|
||||
NSMutableArray *statements = [_info objectAtIndex: 0];
|
||||
unsigned numberOfStatements = [statements count];
|
||||
NSMutableArray *statements;
|
||||
unsigned numberOfStatements;
|
||||
unsigned statement;
|
||||
unsigned pos = 1;
|
||||
NSTimeInterval _duration = [_db durationLogging];
|
||||
NSTimeInterval start = 0.0;
|
||||
|
||||
statements = [NSMutableArray arrayWithCapacity: 100];
|
||||
[self _merge: statements];
|
||||
numberOfStatements = [statements count];
|
||||
|
||||
if (_duration >= 0)
|
||||
{
|
||||
start = GSTickerTimeNow();
|
||||
|
@ -1786,15 +1764,12 @@ static id marker = @"End of statement data";
|
|||
}
|
||||
|
||||
if (numberOfStatements > 1 && ji->addBatch != 0
|
||||
&& [_info count] == numberOfStatements + 1)
|
||||
&& [self _batchable: statements] == YES)
|
||||
{
|
||||
jintArray ja;
|
||||
jint *array;
|
||||
int status = 0;
|
||||
|
||||
/* We have multiple statements without arguments ... so this
|
||||
* is batchable.
|
||||
*/
|
||||
for (statement = 0; statement < numberOfStatements; statement++)
|
||||
{
|
||||
NSString *stmt = [statements objectAtIndex: statement];
|
||||
|
@ -1837,20 +1812,20 @@ static id marker = @"End of statement data";
|
|||
*/
|
||||
for (statement = 0; statement < numberOfStatements; statement++)
|
||||
{
|
||||
NSString *stmt = [statements objectAtIndex: statement];
|
||||
NSArray *info = [statements objectAtIndex: statement];
|
||||
NSString *stmt = [info objectAtIndex: 0];
|
||||
unsigned c = [info count];
|
||||
jmethodID jm;
|
||||
jobject js;
|
||||
|
||||
if ([_info objectAtIndex: pos] == marker)
|
||||
if (c == 1)
|
||||
{
|
||||
pos++; // Step past end of statement data.
|
||||
(*env)->CallIntMethod (env, ji->statement,
|
||||
ji->executeUpdate, JStringFromNSString(env, stmt));
|
||||
}
|
||||
else
|
||||
{
|
||||
NSData *data;
|
||||
int i = 1;
|
||||
unsigned i;
|
||||
jclass jc;
|
||||
|
||||
stmt = [stmt stringByReplacingString: @"'?'''?'"
|
||||
|
@ -1868,9 +1843,12 @@ static id marker = @"End of statement data";
|
|||
|
||||
/* Get data arguments for statement.
|
||||
*/
|
||||
while ((data = [_info objectAtIndex: pos++]) != marker)
|
||||
{
|
||||
(*env)->CallIntMethod (env, js, jm, i++,
|
||||
for (i = 1; i < c; i++)
|
||||
{
|
||||
NSData *data;
|
||||
|
||||
data = [info objectAtIndex: i];
|
||||
(*env)->CallIntMethod (env, js, jm, i,
|
||||
ByteArrayFromNSData(env, data));
|
||||
JException(env);
|
||||
}
|
||||
|
|
56
SQLClient.h
56
SQLClient.h
|
@ -77,6 +77,10 @@
|
|||
Support for standalone web applications ... eg to allow data to be
|
||||
added to the database by people posting web forms to the application.
|
||||
</item>
|
||||
<item>
|
||||
Supports notification of connection to and disconnection from the
|
||||
database server.
|
||||
</item>
|
||||
</list>
|
||||
</section>
|
||||
<section>
|
||||
|
@ -187,6 +191,18 @@
|
|||
@class NSString;
|
||||
@class SQLTransaction;
|
||||
|
||||
/**
|
||||
* Notification sent when an instance becomes connected to the database
|
||||
* server. The notification object is the instance connected.
|
||||
*/
|
||||
NSString *SQLClientDidConnectNotification;
|
||||
|
||||
/**
|
||||
* Notification sent when an instance becomes disconnected from the database
|
||||
* server. The notification object is the instance disconnected.
|
||||
*/
|
||||
NSString *SQLClientDidDisconnectNotification;
|
||||
|
||||
/**
|
||||
* <p>An enhanced array to represent a record returned from a query.
|
||||
* You should <em>NOT</em> try to create instances of this class
|
||||
|
@ -971,6 +987,16 @@ extern unsigned SQLClientTimeTick();
|
|||
*/
|
||||
@interface SQLClient(Convenience)
|
||||
|
||||
/**
|
||||
* Returns a transaction object configured to handle batching and
|
||||
* execute part of a batch of statements if execution of the whole
|
||||
* fails.<br />
|
||||
* If stopOnFailure is YES than execution of the transaction will
|
||||
* stop with the first statement to fail, otherwise it will execute
|
||||
* all the statements it can, skipping any failued statements.
|
||||
*/
|
||||
- (SQLTransaction*) batch: (BOOL)stopOnFailure;
|
||||
|
||||
/**
|
||||
* Executes a query (like the -query:,... method) and checks the result
|
||||
* (raising an exception if the query did not contain a single record)
|
||||
|
@ -998,6 +1024,7 @@ extern unsigned SQLClientTimeTick();
|
|||
* use the receiver as the database connection to perform transactions.
|
||||
*/
|
||||
- (SQLTransaction*) transaction;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
@ -1154,11 +1181,13 @@ extern unsigned SQLClientTimeTick();
|
|||
* database operations should be. If you have multiple threads, you
|
||||
* should create multiple SQLTransaction instances, at least one per thread.
|
||||
*/
|
||||
@interface SQLTransaction : NSObject
|
||||
@interface SQLTransaction : NSObject <NSCopying>
|
||||
{
|
||||
SQLClient *_db;
|
||||
NSMutableArray *_info;
|
||||
unsigned _count;
|
||||
BOOL _batch;
|
||||
BOOL _stop;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1176,7 +1205,7 @@ extern unsigned SQLClientTimeTick();
|
|||
- (void) add: (NSString*)stmt with: (NSDictionary*)values;
|
||||
|
||||
/**
|
||||
* Appends all the statements from the other transaction to the receiver.<br />
|
||||
* Appends a copy of the other transaction to the receiver.<br />
|
||||
* This provides a convenient way of merging transactions which have been
|
||||
* built by different code modules, in order to have them all executed
|
||||
* together in a single operation (for efficiency etc).<br />
|
||||
|
@ -1188,6 +1217,11 @@ extern unsigned SQLClientTimeTick();
|
|||
*/
|
||||
- (void) append: (SQLTransaction*)other;
|
||||
|
||||
/**
|
||||
* Make a copy of the receiver.
|
||||
*/
|
||||
- (id) copyWithZone: (NSZone*)z;
|
||||
|
||||
/**
|
||||
* Returns the number of statements in this transaction.
|
||||
*/
|
||||
|
@ -1218,6 +1252,24 @@ extern unsigned SQLClientTimeTick();
|
|||
*/
|
||||
- (void) execute;
|
||||
|
||||
/**
|
||||
* <p>This is similar to the -execute method, but may allow partial
|
||||
* execution of the transaction if appropriate:
|
||||
* </p>
|
||||
* <p>If the transaction was created using the [SQLClient-batch:] method and
|
||||
* the transaction as a whole fails, individual statements are retried.<br />
|
||||
* The stopOnFailure flag for the batch creation indicates whether the
|
||||
* retries are stopped at the first statement to fail, or continue (skipping
|
||||
* any failed statements).
|
||||
* </p>
|
||||
* <p>If the transaction has had transactions appended to it, those
|
||||
* subsidiary transactions may succeed or fail atomically depending
|
||||
* on their individual attributes.
|
||||
* </p>
|
||||
* The method returns the number of statements which actually succeeded.
|
||||
*/
|
||||
- (unsigned) executeBatch;
|
||||
|
||||
/**
|
||||
* Resets the transaction, removing all previously added statements.
|
||||
* This allows the transaction object to be re-used for multiple
|
||||
|
|
248
SQLClient.m
248
SQLClient.m
|
@ -63,6 +63,12 @@
|
|||
#define SUBCLASS_RESPONSIBILITY
|
||||
#endif
|
||||
|
||||
NSString *SQLClientDidConnectNotification
|
||||
= @"SQLClientDidConnectNotification";
|
||||
|
||||
NSString *SQLClientDidDisconnectNotification
|
||||
= @"SQLClientDidDisconnectNotification";
|
||||
|
||||
static NSNull *null = nil;
|
||||
static Class NSStringClass = 0;
|
||||
static Class NSArrayClass = 0;
|
||||
|
@ -1110,6 +1116,12 @@ static unsigned int maxConnections = 8;
|
|||
NS_ENDHANDLER
|
||||
}
|
||||
[lock unlock];
|
||||
if (connected == YES)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
postNotificationName: SQLClientDidConnectNotification
|
||||
object: self];
|
||||
}
|
||||
}
|
||||
return connected;
|
||||
}
|
||||
|
@ -1190,6 +1202,9 @@ static unsigned int maxConnections = 8;
|
|||
NS_ENDHANDLER
|
||||
}
|
||||
[lock unlock];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
postNotificationName: SQLClientDidDisconnectNotification
|
||||
object: self];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1659,6 +1674,7 @@ static unsigned int maxConnections = 8;
|
|||
|
||||
[lock lock];
|
||||
statement = [info objectAtIndex: 0];
|
||||
|
||||
if ([statement isEqualToString: commitString])
|
||||
{
|
||||
isCommit = YES;
|
||||
|
@ -2292,6 +2308,20 @@ static unsigned int maxConnections = 8;
|
|||
|
||||
@implementation SQLClient(Convenience)
|
||||
|
||||
- (SQLTransaction*) batch: (BOOL)stopOnFailure
|
||||
{
|
||||
TDefs transaction;
|
||||
|
||||
transaction = (TDefs)NSAllocateObject([SQLTransaction class], 0,
|
||||
NSDefaultMallocZone());
|
||||
|
||||
transaction->_db = RETAIN(self);
|
||||
transaction->_info = [NSMutableArray new];
|
||||
transaction->_batch = YES;
|
||||
transaction->_stop = stopOnFailure;
|
||||
return AUTORELEASE((SQLTransaction*)transaction);
|
||||
}
|
||||
|
||||
- (SQLRecord*) queryRecord: (NSString*)stmt, ...
|
||||
{
|
||||
va_list ap;
|
||||
|
@ -2539,101 +2569,209 @@ static unsigned int maxConnections = 8;
|
|||
(_count == 0 ? (id)@"" : (id)[_info objectAtIndex: 0]), _db];
|
||||
}
|
||||
|
||||
- (void) _addInfo: (NSArray*)info
|
||||
{
|
||||
if (_count == 0)
|
||||
{
|
||||
NSMutableString *ms = [[info objectAtIndex: 0] mutableCopy];
|
||||
|
||||
[_info addObjectsFromArray: info];
|
||||
[_info replaceObjectAtIndex: 0 withObject: ms];
|
||||
RELEASE(ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSMutableString *ms = [_info objectAtIndex: 0];
|
||||
unsigned c = [info count];
|
||||
unsigned i = 1;
|
||||
|
||||
[ms appendString: @";"];
|
||||
[ms appendString: [info objectAtIndex: 0]];
|
||||
while (i < c)
|
||||
{
|
||||
[_info addObject: [info objectAtIndex: i++]];
|
||||
}
|
||||
}
|
||||
_count++;
|
||||
}
|
||||
|
||||
- (void) add: (NSString*)stmt,...
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, stmt);
|
||||
[self _addInfo: [_db _prepare: stmt args: ap]];
|
||||
[_info addObject: [_db _prepare: stmt args: ap]];
|
||||
_count++;
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
- (void) add: (NSString*)stmt with: (NSDictionary*)values
|
||||
{
|
||||
[self _addInfo: [_db _substitute: stmt with: values]];
|
||||
[_info addObject: [_db _substitute: stmt with: values]];
|
||||
_count++;
|
||||
}
|
||||
|
||||
- (void) append: (SQLTransaction*)other
|
||||
{
|
||||
if (other != nil && other->_count > 0)
|
||||
{
|
||||
[self _addInfo: other->_info];
|
||||
other = [other copy];
|
||||
[_info addObject: other];
|
||||
_count += other->_count;
|
||||
RELEASE(other);
|
||||
}
|
||||
}
|
||||
|
||||
- (id) copyWithZone: (NSZone*)z
|
||||
{
|
||||
SQLTransaction *c;
|
||||
|
||||
c = (SQLTransaction*)NSCopyObject(self, 0, z);
|
||||
c->_info = [c->_info mutableCopy];
|
||||
return c;
|
||||
}
|
||||
|
||||
- (SQLClient*) db
|
||||
{
|
||||
return _db;
|
||||
}
|
||||
|
||||
- (void) _addSQL: (NSMutableString*)sql andArgs: (NSMutableArray*)args
|
||||
{
|
||||
unsigned count = [_info count];
|
||||
unsigned index;
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
id o = [_info objectAtIndex: index];
|
||||
|
||||
if ([o isKindOfClass: NSArrayClass] == YES)
|
||||
{
|
||||
unsigned c = [(NSArray*)o count];
|
||||
|
||||
if (c > 0)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
[sql appendString: [(NSArray*)o objectAtIndex: 0]];
|
||||
[sql appendString: @";"];
|
||||
for (i = 1; i < c; i++)
|
||||
{
|
||||
[args addObject: [(NSArray*)o objectAtIndex: i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[(SQLTransaction*)o _addSQL: sql andArgs: args];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _countLength: (unsigned*)length andArgs: (unsigned*)args
|
||||
{
|
||||
unsigned count = [_info count];
|
||||
unsigned index;
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
id o = [_info objectAtIndex: index];
|
||||
|
||||
if ([o isKindOfClass: NSArrayClass] == YES)
|
||||
{
|
||||
unsigned c = [(NSArray*)o count];
|
||||
|
||||
if (c > 0)
|
||||
{
|
||||
length += [[(NSArray*)o objectAtIndex: 0] length] + 1;
|
||||
args += c - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[(SQLTransaction*)o _countLength: length andArgs: args];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) execute
|
||||
{
|
||||
if (_count > 0)
|
||||
{
|
||||
BOOL wrapped = NO;
|
||||
NSMutableString *sql = [_info objectAtIndex: 0];
|
||||
NSMutableArray *info = nil;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
if (_count > 1 && [_db isInTransaction] == NO)
|
||||
{
|
||||
wrapped = YES;
|
||||
[sql replaceCharactersInRange: NSMakeRange(0, 0)
|
||||
withString: @"begin;"];
|
||||
[sql replaceCharactersInRange: NSMakeRange([sql length], 0)
|
||||
withString: @";commit"];
|
||||
}
|
||||
[_db simpleExecute: _info];
|
||||
if (wrapped == YES)
|
||||
{
|
||||
wrapped = NO;
|
||||
[sql replaceCharactersInRange: NSMakeRange([sql length] - 7, 7)
|
||||
withString: @""];
|
||||
[sql replaceCharactersInRange: NSMakeRange(0, 6)
|
||||
withString: @""];
|
||||
}
|
||||
NSMutableString *sql;
|
||||
unsigned sqlSize = 0;
|
||||
unsigned argCount = 0;
|
||||
|
||||
[self _countLength: &sqlSize andArgs: &argCount];
|
||||
|
||||
/* Allocate and initialise the transaction statement.
|
||||
*/
|
||||
info = [[NSMutableArray alloc] initWithCapacity: argCount + 1];
|
||||
sql = [[NSMutableString alloc] initWithCapacity: sqlSize + 13];
|
||||
[info addObject: sql];
|
||||
RELEASE(sql);
|
||||
if ([_db isInTransaction] == NO)
|
||||
{
|
||||
[sql appendString: @"begin;"];
|
||||
}
|
||||
|
||||
[self _addSQL: sql andArgs: info];
|
||||
|
||||
if ([_db isInTransaction] == NO)
|
||||
{
|
||||
[sql appendString: @"commit;"];
|
||||
}
|
||||
|
||||
[_db simpleExecute: info];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
if (wrapped == YES)
|
||||
{
|
||||
[sql replaceCharactersInRange: NSMakeRange([sql length] - 7, 7)
|
||||
withString: @""];
|
||||
[sql replaceCharactersInRange: NSMakeRange(0, 6)
|
||||
withString: @""];
|
||||
}
|
||||
RELEASE(info);
|
||||
[localException raise];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
}
|
||||
|
||||
- (unsigned) executeBatch
|
||||
{
|
||||
unsigned executed = 0;
|
||||
|
||||
if (_count > 0)
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
[self execute];
|
||||
executed = _count;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
if (_batch == YES)
|
||||
{
|
||||
unsigned count = [_info count];
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
BOOL success = NO;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
id o = [_info objectAtIndex: i];
|
||||
|
||||
if ([o isKindOfClass: NSArrayClass] == YES)
|
||||
{
|
||||
[_db simpleExecute: (NSArray*)o];
|
||||
executed++;
|
||||
success = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned result;
|
||||
|
||||
result = [(SQLTransaction*)o executeBatch];
|
||||
executed += result;
|
||||
if (result == [(SQLTransaction*)o count])
|
||||
{
|
||||
success = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
success = NO;
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
if (success == NO && _stop == YES)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
return executed;
|
||||
}
|
||||
|
||||
- (void) reset
|
||||
{
|
||||
[_info removeAllObjects];
|
||||
|
|
Loading…
Reference in a new issue