mirror of
https://github.com/gnustep/libs-sqlclient.git
synced 2025-02-21 19:01:03 +00:00
first attempt at merge code
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/sqlclient/trunk@38041 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
f537fcc33d
commit
ff5a0b347f
3 changed files with 222 additions and 5 deletions
|
@ -1,3 +1,9 @@
|
||||||
|
2014-08-08 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
|
* SQLClient.h:
|
||||||
|
* SQLClient.m:
|
||||||
|
Add merging of insert/update statements in a transaction.
|
||||||
|
|
||||||
2014-07-17 Yavor Doganov <yavor@gnu.org>
|
2014-07-17 Yavor Doganov <yavor@gnu.org>
|
||||||
|
|
||||||
Install bundles in a versioned directory.
|
Install bundles in a versioned directory.
|
||||||
|
|
36
SQLClient.h
36
SQLClient.h
|
@ -1572,7 +1572,7 @@ SQLCLIENT_PRIVATE
|
||||||
- (id) copyWithZone: (NSZone*)z;
|
- (id) copyWithZone: (NSZone*)z;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of individual statements ond/r subsidiary transactions
|
* Returns the number of individual statements and/or subsidiary transactions
|
||||||
* which have been added to the receiver. For a count of the total number
|
* which have been added to the receiver. For a count of the total number
|
||||||
* of statements, use the -totalCount method.
|
* of statements, use the -totalCount method.
|
||||||
*/
|
*/
|
||||||
|
@ -1649,6 +1649,40 @@ SQLCLIENT_PRIVATE
|
||||||
*/
|
*/
|
||||||
- (void) insertTransaction: (SQLTransaction*)trn atIndex: (unsigned)index;
|
- (void) insertTransaction: (SQLTransaction*)trn atIndex: (unsigned)index;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like -add:... but, if the new statement can be merged with a recently
|
||||||
|
* added one, this does that rather than adding as a separate statement.
|
||||||
|
* <p>You may use this 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 statement of the form:<br />
|
||||||
|
* UPDATE table SET settings WHERE condition;<br />
|
||||||
|
* So that statements may be merged into:<br />
|
||||||
|
* UPDATE table SET setting 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. Only the most recent five statements in a transaction are checked
|
||||||
|
* for eligibility.<br />
|
||||||
|
* 3. Merging is done only if the statement up to the string 'VALUES'
|
||||||
|
* (for insert) or 'WHERE' (for update) matches.<br />
|
||||||
|
* 4. This is a simple text match rather than sql syntactic analysis,
|
||||||
|
* so it's possible to confuse the process with complex statements.
|
||||||
|
*/
|
||||||
|
- (void) merge: (NSString*)stmt,...;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like -add:with: but, if the new statement can be merged with a recently
|
||||||
|
* added one, this does that rather than adding as a separate statement.
|
||||||
|
* See -merge:,... for meore details.
|
||||||
|
*/
|
||||||
|
- (void) merge: (NSString*)stmt with: (NSDictionary*)values;
|
||||||
|
|
||||||
/** Remove the index'th transaction or statement from the receiver.
|
/** Remove the index'th transaction or statement from the receiver.
|
||||||
*/
|
*/
|
||||||
- (void) removeTransactionAtIndex: (unsigned)index;
|
- (void) removeTransactionAtIndex: (unsigned)index;
|
||||||
|
|
185
SQLClient.m
185
SQLClient.m
|
@ -650,9 +650,9 @@ static NSArray *rollbackStatement = nil;
|
||||||
@interface SQLClient (Private)
|
@interface SQLClient (Private)
|
||||||
- (void) _configure: (NSNotification*)n;
|
- (void) _configure: (NSNotification*)n;
|
||||||
- (void) _populateCache: (CacheQuery*)a;
|
- (void) _populateCache: (CacheQuery*)a;
|
||||||
- (NSArray*) _prepare: (NSString*)stmt args: (va_list)args;
|
- (NSMutableArray*) _prepare: (NSString*)stmt args: (va_list)args;
|
||||||
- (void) _recordMainThread;
|
- (void) _recordMainThread;
|
||||||
- (NSArray*) _substitute: (NSString*)str with: (NSDictionary*)vals;
|
- (NSMutableArray*) _substitute: (NSString*)str with: (NSDictionary*)vals;
|
||||||
+ (void) _tick: (NSTimer*)t;
|
+ (void) _tick: (NSTimer*)t;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -2236,7 +2236,7 @@ static unsigned int maxConnections = 8;
|
||||||
* any NSData objects following. The NSData objects appear in the
|
* any NSData objects following. The NSData objects appear in the
|
||||||
* statement strings as the marker sequence - <code>'?'''?'</code>
|
* statement strings as the marker sequence - <code>'?'''?'</code>
|
||||||
*/
|
*/
|
||||||
- (NSArray*) _prepare: (NSString*)stmt args: (va_list)args
|
- (NSMutableArray*) _prepare: (NSString*)stmt args: (va_list)args
|
||||||
{
|
{
|
||||||
NSMutableArray *ma = [NSMutableArray arrayWithCapacity: 2];
|
NSMutableArray *ma = [NSMutableArray arrayWithCapacity: 2];
|
||||||
NSString *tmp = va_arg(args, NSString*);
|
NSString *tmp = va_arg(args, NSString*);
|
||||||
|
@ -2290,7 +2290,7 @@ static unsigned int maxConnections = 8;
|
||||||
* any NSData objects following. The NSData objects appear in the
|
* any NSData objects following. The NSData objects appear in the
|
||||||
* statement strings as the marker sequence - <code>'?'''?'</code>
|
* statement strings as the marker sequence - <code>'?'''?'</code>
|
||||||
*/
|
*/
|
||||||
- (NSArray*) _substitute: (NSString*)str with: (NSDictionary*)vals
|
- (NSMutableArray*) _substitute: (NSString*)str with: (NSDictionary*)vals
|
||||||
{
|
{
|
||||||
unsigned int l = [str length];
|
unsigned int l = [str length];
|
||||||
NSRange r;
|
NSRange r;
|
||||||
|
@ -3202,6 +3202,183 @@ static unsigned int maxConnections = 8;
|
||||||
[trn release];
|
[trn release];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Try to merge the prepared statement p with an earlier statement in the
|
||||||
|
* transaction. We search up to 5 earlier statements and we merge if;
|
||||||
|
* a. We have something like 'INSERT INTO table (fields) VALUES (values)'
|
||||||
|
* where everything up to 'VALUES' is the same, so we can built a multiline
|
||||||
|
* insert like 'INSERT INTO table (fields) VALUES (values1),(values2),...'
|
||||||
|
* b. We have something like 'UPDATE table SET settings WHERE condition'
|
||||||
|
* where everything up to the condition is the same, so we can build
|
||||||
|
* 'UPDATE table SET settings WHERE condition1 OR (condition2) OR ...'
|
||||||
|
*/
|
||||||
|
- (void) _merge: (NSMutableArray*)p
|
||||||
|
{
|
||||||
|
if (_count > 0)
|
||||||
|
{
|
||||||
|
static NSCharacterSet *w = nil;
|
||||||
|
NSString *s;
|
||||||
|
NSRange r;
|
||||||
|
|
||||||
|
s = [p objectAtIndex: 0]; // Get SQL part of array
|
||||||
|
|
||||||
|
if (nil == w)
|
||||||
|
{
|
||||||
|
w = [[NSCharacterSet whitespaceAndNewlineCharacterSet] retain];
|
||||||
|
}
|
||||||
|
|
||||||
|
r = [s rangeOfString: @"INSERT" options: NSCaseInsensitiveSearch];
|
||||||
|
if (r.length > 0 && 0 == r.location)
|
||||||
|
{
|
||||||
|
r = [s rangeOfString: @"VALUES" options: NSCaseInsensitiveSearch];
|
||||||
|
if (r.length > 0)
|
||||||
|
{
|
||||||
|
NSUInteger l = [s length];
|
||||||
|
NSUInteger pos = NSMaxRange(r);
|
||||||
|
|
||||||
|
while (pos < l
|
||||||
|
&& [w characterIsMember: [s characterAtIndex: pos]])
|
||||||
|
{
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
if (pos < l && [s characterAtIndex: pos] == '(')
|
||||||
|
{
|
||||||
|
NSString *t = [s substringToIndex: pos];
|
||||||
|
NSUInteger index = _count;
|
||||||
|
NSUInteger attempts = 0;
|
||||||
|
|
||||||
|
s = [s substringFromIndex: pos];
|
||||||
|
while (index-- > 0 && attempts++ < 5)
|
||||||
|
{
|
||||||
|
NSMutableArray *o;
|
||||||
|
NSString *os;
|
||||||
|
|
||||||
|
o = [_info objectAtIndex: index];
|
||||||
|
os = [o objectAtIndex: 0];
|
||||||
|
if ([os hasPrefix: t])
|
||||||
|
{
|
||||||
|
NSMutableString *m;
|
||||||
|
|
||||||
|
if ([os isKindOfClass: [NSMutableString class]])
|
||||||
|
{
|
||||||
|
m = (NSMutableString*)os;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m = [[os mutableCopy] autorelease];
|
||||||
|
}
|
||||||
|
[m appendString: @","];
|
||||||
|
[m appendString: s];
|
||||||
|
[o replaceObjectAtIndex: 0 withObject: m];
|
||||||
|
for (index = 1; index < [p count]; index++)
|
||||||
|
{
|
||||||
|
[o addObject: [p objectAtIndex: index]];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = [s rangeOfString: @"UPDATE" options: NSCaseInsensitiveSearch];
|
||||||
|
if (r.length > 0 && 0 == r.location)
|
||||||
|
{
|
||||||
|
r = [s rangeOfString: @"WHERE" options: NSCaseInsensitiveSearch];
|
||||||
|
if (r.length > 0)
|
||||||
|
{
|
||||||
|
NSUInteger l = [s length];
|
||||||
|
NSUInteger pos = NSMaxRange(r);
|
||||||
|
|
||||||
|
while (pos < l
|
||||||
|
&& [w characterIsMember: [s characterAtIndex: pos]])
|
||||||
|
{
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
if (pos < l && [s characterAtIndex: pos] == '(')
|
||||||
|
{
|
||||||
|
NSString *t = [s substringToIndex: pos];
|
||||||
|
NSUInteger index = _count;
|
||||||
|
NSUInteger attempts = 0;
|
||||||
|
|
||||||
|
/* Get the condition after the WHERE and if it's not
|
||||||
|
* in brackets, add them so the merge can work.
|
||||||
|
*/
|
||||||
|
s = [s substringFromIndex: pos];
|
||||||
|
if ([s characterAtIndex: 0] != '(')
|
||||||
|
{
|
||||||
|
s = [NSString stringWithFormat: @"(%@)", s];
|
||||||
|
}
|
||||||
|
|
||||||
|
while (index-- > 0 && attempts++ < 5)
|
||||||
|
{
|
||||||
|
NSMutableArray *o;
|
||||||
|
NSString *os;
|
||||||
|
|
||||||
|
o = [_info objectAtIndex: index];
|
||||||
|
os = [o objectAtIndex: 0];
|
||||||
|
if ([os hasPrefix: t])
|
||||||
|
{
|
||||||
|
NSMutableString *m;
|
||||||
|
|
||||||
|
l = [os length];
|
||||||
|
if ([os characterAtIndex: l - 1] == ')')
|
||||||
|
{
|
||||||
|
if ([os isKindOfClass: [NSMutableString class]])
|
||||||
|
{
|
||||||
|
m = (NSMutableString*)os;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m = [[os mutableCopy] autorelease];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* The condition of the WHERE clause was not
|
||||||
|
* bracketed, so we extract it and build a
|
||||||
|
* new statement in which it is bracketed.
|
||||||
|
*/
|
||||||
|
os = [os substringFromIndex: pos];
|
||||||
|
m = [NSMutableString stringWithFormat:
|
||||||
|
@"%@(%@)", t, os];
|
||||||
|
}
|
||||||
|
[m appendString: @" OR "];
|
||||||
|
[m appendString: s];
|
||||||
|
[o replaceObjectAtIndex: 0 withObject: m];
|
||||||
|
for (index = 1; index < [p count]; index++)
|
||||||
|
{
|
||||||
|
[o addObject: [p objectAtIndex: index]];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[_info addObject: p];
|
||||||
|
_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) merge: (NSString*)stmt,...
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
NSMutableArray *p;
|
||||||
|
|
||||||
|
va_start (ap, stmt);
|
||||||
|
p = [_db _prepare: stmt args: ap];
|
||||||
|
va_end (ap);
|
||||||
|
[self _merge: p];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) merge: (NSString*)stmt with: (NSDictionary*)values
|
||||||
|
{
|
||||||
|
NSMutableArray *p;
|
||||||
|
|
||||||
|
p = [_db _substitute: stmt with: values];
|
||||||
|
[self _merge: p];
|
||||||
|
}
|
||||||
|
|
||||||
- (void) removeTransactionAtIndex: (unsigned)index
|
- (void) removeTransactionAtIndex: (unsigned)index
|
||||||
{
|
{
|
||||||
id o;
|
id o;
|
||||||
|
|
Loading…
Reference in a new issue