mirror of
https://github.com/gnustep/libs-sqlclient.git
synced 2025-02-20 18:32:06 +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>
|
||||
|
||||
Install bundles in a versioned directory.
|
||||
|
|
36
SQLClient.h
36
SQLClient.h
|
@ -1572,7 +1572,7 @@ SQLCLIENT_PRIVATE
|
|||
- (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
|
||||
* of statements, use the -totalCount method.
|
||||
*/
|
||||
|
@ -1649,6 +1649,40 @@ SQLCLIENT_PRIVATE
|
|||
*/
|
||||
- (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.
|
||||
*/
|
||||
- (void) removeTransactionAtIndex: (unsigned)index;
|
||||
|
|
185
SQLClient.m
185
SQLClient.m
|
@ -650,9 +650,9 @@ static NSArray *rollbackStatement = nil;
|
|||
@interface SQLClient (Private)
|
||||
- (void) _configure: (NSNotification*)n;
|
||||
- (void) _populateCache: (CacheQuery*)a;
|
||||
- (NSArray*) _prepare: (NSString*)stmt args: (va_list)args;
|
||||
- (NSMutableArray*) _prepare: (NSString*)stmt args: (va_list)args;
|
||||
- (void) _recordMainThread;
|
||||
- (NSArray*) _substitute: (NSString*)str with: (NSDictionary*)vals;
|
||||
- (NSMutableArray*) _substitute: (NSString*)str with: (NSDictionary*)vals;
|
||||
+ (void) _tick: (NSTimer*)t;
|
||||
@end
|
||||
|
||||
|
@ -2236,7 +2236,7 @@ static unsigned int maxConnections = 8;
|
|||
* any NSData objects following. The NSData objects appear in the
|
||||
* 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];
|
||||
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
|
||||
* 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];
|
||||
NSRange r;
|
||||
|
@ -3202,6 +3202,183 @@ static unsigned int maxConnections = 8;
|
|||
[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
|
||||
{
|
||||
id o;
|
||||
|
|
Loading…
Reference in a new issue