Author: rfm
Date: Fri Aug  8 10:07:06 2014
New Revision: 38041

URL: http://svn.gna.org/viewcvs/gnustep?rev=38041&view=rev
Log:
first attempt at merge code

Modified:
    libs/sqlclient/trunk/ChangeLog
    libs/sqlclient/trunk/SQLClient.h
    libs/sqlclient/trunk/SQLClient.m

Modified: libs/sqlclient/trunk/ChangeLog
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/sqlclient/trunk/ChangeLog?rev=38041&r1=38040&r2=38041&view=diff
==============================================================================
--- libs/sqlclient/trunk/ChangeLog      (original)
+++ libs/sqlclient/trunk/ChangeLog      Fri Aug  8 10:07:06 2014
@@ -1,3 +1,9 @@
+2014-08-08 Richard Frith-Macdonald  <[email protected]>
+
+       * SQLClient.h:
+       * SQLClient.m:
+       Add merging of insert/update statements in a transaction.
+       
 2014-07-17  Yavor Doganov  <[email protected]>
 
        Install bundles in a versioned directory.

Modified: libs/sqlclient/trunk/SQLClient.h
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/sqlclient/trunk/SQLClient.h?rev=38041&r1=38040&r2=38041&view=diff
==============================================================================
--- libs/sqlclient/trunk/SQLClient.h    (original)
+++ libs/sqlclient/trunk/SQLClient.h    Fri Aug  8 10:07:06 2014
@@ -1572,7 +1572,7 @@
 - (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.
  */
@@ -1648,6 +1648,40 @@
  * database client as the receiver.
  */
 - (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.
  */

Modified: libs/sqlclient/trunk/SQLClient.m
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/sqlclient/trunk/SQLClient.m?rev=38041&r1=38040&r2=38041&view=diff
==============================================================================
--- libs/sqlclient/trunk/SQLClient.m    (original)
+++ libs/sqlclient/trunk/SQLClient.m    Fri Aug  8 10:07:06 2014
@@ -650,9 +650,9 @@
 @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 @@
  * 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 @@
  * 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 @@
   [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;


_______________________________________________
Gnustep-cvs mailing list
[email protected]
https://mail.gna.org/listinfo/gnustep-cvs

Reply via email to