Author: rfm
Date: Wed Mar 11 18:16:14 2015
New Revision: 38400

URL: http://svn.gna.org/viewcvs/gnustep?rev=38400&view=rev
Log:
Add preliminary array support

Modified:
    libs/sqlclient/trunk/ChangeLog
    libs/sqlclient/trunk/Postgres.m
    libs/sqlclient/trunk/SQLClient.h
    libs/sqlclient/trunk/SQLClient.m
    libs/sqlclient/trunk/SQLClientPool.m
    libs/sqlclient/trunk/testPostgres.m

Modified: libs/sqlclient/trunk/ChangeLog
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/sqlclient/trunk/ChangeLog?rev=38400&r1=38399&r2=38400&view=diff
==============================================================================
--- libs/sqlclient/trunk/ChangeLog      (original)
+++ libs/sqlclient/trunk/ChangeLog      Wed Mar 11 18:16:14 2015
@@ -1,3 +1,12 @@
+2015-03-11 Richard Frith-Macdonald  <[email protected]>
+       * SQLClientPool.m: Fixup for ewxposing prepare method
+       * SQLClient.h:
+       * SQLClient.m:
+       * Postgres.m:
+       * testPostgres.m:
+       Add simple array support for char/varchar/text, integer/real,
+       timestamp, bool and bytea.
+
 2015-03-02 Richard Frith-Macdonald  <[email protected]>
 
        * Postgres.h: Drop support for old versions of postgres which didn't

Modified: libs/sqlclient/trunk/Postgres.m
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/sqlclient/trunk/Postgres.m?rev=38400&r1=38399&r2=38400&view=diff
==============================================================================
--- libs/sqlclient/trunk/Postgres.m     (original)
+++ libs/sqlclient/trunk/Postgres.m     Wed Mar 11 18:16:14 2015
@@ -485,6 +485,221 @@
   return (str - start);
 }
 
+- (char*) parseIntoArray: (NSMutableArray *)a type: (int)t from: (char*)p
+{
+  p++;  /* Step past '{' */
+  while (*p && *p != '}')
+    {
+      id        v = nil;
+
+      /* Ignore leading space before field data.
+       */
+      while (isspace(*p))
+        {
+          p++;
+        }
+      if ('{' == *p)
+        {
+          /* Found a nested array.
+           */
+          v = [[NSMutableArray alloc] initWithCapacity: 10];
+          p = [self parseIntoArray: v type: t from: p]; 
+        }
+      else if ('\"' == *p)
+        {
+          char  *start = ++p;
+          int   len = 0;
+        
+          /* Found something quoted (char, varchar, text, bytea etc)
+           */
+          while (*p != '\0' && *p != '\"')
+            {
+              if ('\'' == *p)
+                {
+                  p++;
+                }
+              p++;
+              len++;
+            }
+          if ('\"' == *p)
+            {
+              *p++ = '\0';
+            }
+          if (len == (p - start + 1))
+            {
+              v = [[NSString alloc] initWithUTF8String: start];
+            }
+          else
+            {
+              char      *buf;
+              char      *ptr;
+              int       i;
+
+              buf = malloc(len+1);
+              ptr = start;
+              i  = 0;
+              while (*ptr != '\0')
+                {
+                  if ('\\' == *ptr)
+                    {
+                      ptr++;
+                    }
+                  buf[i++] = *ptr++;
+                }
+              buf[len] = '\0';
+              if ('D' == t)
+                {
+                  /* This is expected to be bytea data
+                   */
+                  v = [[self dataFromBLOB: buf] retain];
+                }
+              else
+                {
+                  v = [NSString alloc];
+                  v = [v initWithBytesNoCopy: buf
+                                      length: len
+                                    encoding: NSUTF8StringEncoding
+                                freeWhenDone: YES];
+                }
+            }
+        }
+      else
+        {
+          char  *start = p;
+          char  save;
+          int   len;
+
+          /* This is an unquoted field ... could be NULL or a boolean,
+           * or a numeric field, or a timestamp or just a simple string.
+           */
+          while (*p != '\0' && *p != ',' && *p != '}')
+            {
+              p++;
+            }
+          save = *p;
+          *p = '\0'; 
+          len = trim(start);
+          if (strcmp(start, "NULL") == 0)
+            {
+              v = null;
+            }
+          else if ('T' == t)
+            {
+              v = [[self dbToDateFromBuffer: start length: len] retain];
+            }
+          else if ('D' == t)
+            {
+              v = [[self dataFromBLOB: start] retain];
+            }
+          else if ('B' == t)
+            {
+              if (*start == 't')
+                v = @"YES";
+              else
+                v = @"NO";
+            }
+          else
+            {
+              v = [[NSString alloc] initWithUTF8String: start];
+            }
+          *p = save;
+        }
+      if (nil != v)
+        {
+          [a addObject: v];
+          [v release];
+        }
+      if (',' == *p)
+        {
+          p++;
+        }
+    }
+  if ('}' == *p)
+    {
+      p++;
+    }
+  return p;
+}
+
+- (id) parseField: (char *)p type: (int)t
+{
+  char  arrayType = 0;
+
+  switch (t)
+    {
+      case 1082:       // Date
+        return [self dbToDateFromBuffer: p length: trim(p)];
+
+      case 1083:       // Time (treat as string)
+        trim(p);
+        return [NSString stringWithUTF8String: p];
+
+      case 1114:       // Timestamp without time zone.
+      case 1184:       // Timestamp with time zone.
+        return [self dbToDateFromBuffer: p length: trim(p)];
+
+      case 16:         // BOOL
+        if (*p == 't')
+          {
+            return @"YES";
+          }
+        else
+          {
+            return @"NO";
+          }
+
+      case 17:         // BYTEA
+        return [self dataFromBLOB: p];
+
+      case 18:          // "char"
+        return [NSString stringWithUTF8String: p];
+
+      case 20:          // INT8
+      case 21:          // INT2
+      case 23:          // INT4
+        trim(p);
+        return [NSString stringWithUTF8String: p];
+        break;
+
+      case 1182:       // DATE ARRAY
+      case 1115:       // TS without TZ ARRAY
+      case 1185:       // TS with TZ ARRAY
+        if (0 == arrayType) arrayType = 'T';    // Timestamp
+      case 1000:        // BOOL ARRAY
+        if (0 == arrayType) arrayType = 'B';    // Boolean
+      case 1001:        // BYTEA ARRAY
+        if (0 == arrayType) arrayType = 'D';    // Data
+      case 1005:        // INT2 ARRAY
+      case 1007:        // INT4 ARRAY
+      case 1016:        // INT8 ARRAY
+      case 1021:        // FLOAT ARRAY
+      case 1022:        // DOUBLE ARRAY
+      case 1002:        // CHAR ARRAY
+      case 1009:        // TEXT ARRAY
+      case 1015:        // VARCHAR ARRAY
+        if ('{' == *p)
+          {
+            NSMutableArray      *a;
+
+            a = [NSMutableArray arrayWithCapacity: 10];
+            p = [self parseIntoArray: a type: arrayType from: p];
+            if ([self debugging] > 2)
+              {
+                NSLog(@"Parsed array is %@", a);
+              }
+            return a;
+          }
+
+      case 25:          // TEXT
+      default:
+        if (YES == _shouldTrim)
+          {
+            trim(p);
+          }
+        return [NSString stringWithUTF8String: p];
+    }
+}
+
 - (NSMutableArray*) backendQuery: (NSString*)stmt
                      recordType: (id)rtype
                        listType: (id)ltype
@@ -542,17 +757,17 @@
          int           recordCount = PQntuples(result);
          int           fieldCount = PQnfields(result);
          NSString      *keys[fieldCount];
-         int           types[fieldCount];
-         int           modifiers[fieldCount];
-         int           formats[fieldCount];
+         int           ftype[fieldCount];
+         int           fmod[fieldCount];
+         int           fformat[fieldCount];
          int           i;
 
          for (i = 0; i < fieldCount; i++)
            {
              keys[i] = [NSString stringWithUTF8String: PQfname(result, i)];
-             types[i] = PQftype(result, i);
-             modifiers[i] = PQfmod(result, i);
-             formats[i] = PQfformat(result, i);
+             ftype[i] = PQftype(result, i);
+             fmod[i] = PQfmod(result, i);
+             fformat[i] = PQfformat(result, i);
            }
 
          records = [[ltype alloc] initWithCapacity: recordCount];
@@ -574,69 +789,17 @@
                      if ([self debugging] > 1)
                        { 
                          [self debug: @"%@ type:%d mod:%d size: %d\n",
-                           keys[j], types[j], modifiers[j], size];
+                           keys[j], ftype[j], fmod[j], size];
                        }
-                     if (formats[j] == 0)      // Text
+                     if (fformat[j] == 0)      // Text
                        {
-                         switch (types[j])
-                           {
-                             case 1082:        // Date
-                               v = [self dbToDateFromBuffer: p
-                                                     length: trim(p)];
-                               break;
-        
-                             case 1083:        // Time (treat as string)
-                               trim(p);
-                               v = [NSString stringWithUTF8String: p];
-                                break;
-
-                             case 1114:        // Timestamp without time zone.
-                             case 1184:        // Timestamp with time zone.
-                               v = [self dbToDateFromBuffer: p
-                                                     length: trim(p)];
-                               break;
-
-                             case 16:          // BOOL
-                               if (*p == 't')
-                                 {
-                                   v = @"YES";
-                                 }
-                               else
-                                 {
-                                   v = @"NO";
-                                 }
-                               break;
-
-                             case 17:          // BYTEA
-                               v = [self dataFromBLOB: p];
-                               break;
-
-                              case 18:          // "char"
-                               v = [NSString stringWithUTF8String: p];
-                                break;
-
-                              case 20:          // INT8
-                              case 21:          // INT2
-                              case 23:          // INT4
-                               trim(p);
-                               v = [NSString stringWithUTF8String: p];
-                               break;
-
-                              case 25:          // TEXT
-                             default:
-                                if (YES == _shouldTrim)
-                                  {
-                                    trim(p);
-                                  }
-                               v = [NSString stringWithUTF8String: p];
-                               break;
-                           }
+                          v = [self parseField: p type: ftype[j]];
                        }
                      else                      // Binary
                        {
                          NSLog(@"Binary data treated as NSNull "
                            @"in %@ type:%d mod:%d size:%d\n",
-                           keys[j], types[j], modifiers[j], size);
+                           keys[j], ftype[j], fmod[j], size);
                        }
                    }
                  values[j] = v;
@@ -1012,6 +1175,66 @@
   [super dealloc];
 }
 
+- (NSMutableString*) quoteArray: (NSArray *)a
+                       toString: (NSMutableString *)s
+                 quotingStrings: (BOOL)q
+{
+  NSUInteger    count;
+  NSUInteger    index;
+
+  NSAssert([a isKindOfClass: [NSArray class]], NSInvalidArgumentException);
+  if (nil == s)
+    {
+      s = [NSMutableString stringWithCapacity: 1000];
+    }
+  [s appendString: @"ARRAY["];
+  count = [a count];
+  for (index = 0; index < count; index++)
+    {
+      id        o = [a objectAtIndex: index];
+
+      if (index > 0)
+        {
+          [s appendString: @","];
+        }
+      if ([o isKindOfClass: [NSArray class]])
+        {
+          [self quoteArray: (NSArray *)o toString: s quotingStrings: q];
+        }
+      else if ([o isKindOfClass: [NSString class]])
+        {
+          if (YES == q)
+            {
+              o = [self quoteString: (NSString*)o];
+            }
+          [s appendString: (NSString*)o];
+        }
+      else if ([o isKindOfClass: [NSDate class]])
+        {
+          [s appendString: [self quote: (NSString*)o]];
+          [s appendString: @"::timestamp"];
+        }
+      else if ([o isKindOfClass: [NSData class]])
+        {
+          unsigned      len = [self lengthOfEscapedBLOB: o];
+          uint8_t       *buf;
+
+          buf = malloc(len+1);
+          [self copyEscapedBLOB: o into: buf];
+          buf[len] = '\0';
+          [s appendFormat: @"%s::bytea", buf];
+          free(buf);
+        }
+      else
+        {
+          o = [self quote: (NSString*)o];
+          [s appendString: (NSString*)o];
+        }
+    }
+  [s appendString: @"]"];
+  return s;
+}
+
 - (NSString*) quoteString: (NSString *)s
 {
   NSData       *d = [s dataUsingEncoding: NSUTF8StringEncoding];

Modified: libs/sqlclient/trunk/SQLClient.h
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/sqlclient/trunk/SQLClient.h?rev=38400&r1=38399&r2=38400&view=diff
==============================================================================
--- libs/sqlclient/trunk/SQLClient.h    (original)
+++ libs/sqlclient/trunk/SQLClient.h    Wed Mar 11 18:16:14 2015
@@ -796,6 +796,18 @@
  */
 - (NSString*) quotef: (NSString*)fmt, ...;
 
+/* Produce a quoted string from an array on databases where arrays are
+ * supported (currently only Postgres).<br />
+ * If the s argument is not nil, the quoted array is appended to it rather
+ * than being produced in a new string (this method uses that feature to
+ * recursively quote nested arrays).<br />
+ * The q argument determines whether string values found in the array
+ * are quoted or added literally.
+ */
+- (NSMutableString*) quoteArray: (NSArray *)a
+                       toString: (NSMutableString *)s
+                 quotingStrings: (BOOL)q;
+
 /**
  * Convert a big (64 bit) integer to a string suitable for use in an SQL query.
  */

Modified: libs/sqlclient/trunk/SQLClient.m
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/sqlclient/trunk/SQLClient.m?rev=38400&r1=38399&r2=38400&view=diff
==============================================================================
--- libs/sqlclient/trunk/SQLClient.m    (original)
+++ libs/sqlclient/trunk/SQLClient.m    Wed Mar 11 18:16:14 2015
@@ -1565,6 +1565,15 @@
   quoted = [self quoteString: str];
   [str release];
   return quoted;
+}
+
+- (NSMutableString*) quoteArray: (NSArray *)a
+                       toString: (NSMutableString *)s
+                 quotingStrings: (BOOL)q
+{
+  [NSException raise: NSGenericException
+    format: @"%@ not supported for this database", 
NSStringFromSelector(_cmd)]; 
+  return nil;
 }
 
 - (NSString*) quoteBigInteger: (int64_t)i

Modified: libs/sqlclient/trunk/SQLClientPool.m
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/sqlclient/trunk/SQLClientPool.m?rev=38400&r1=38399&r2=38400&view=diff
==============================================================================
--- libs/sqlclient/trunk/SQLClientPool.m        (original)
+++ libs/sqlclient/trunk/SQLClientPool.m        Wed Mar 11 18:16:14 2015
@@ -516,10 +516,6 @@
 
 @end
 
-@interface      SQLClient (Private)
-- (NSMutableArray*) _prepare: (NSString*)stmt args: (va_list)args;
-@end
-
 @implementation SQLClientPool (ConvenienceMethods)
 
 - (NSString*) buildQuery: (NSString*)stmt, ...
@@ -532,7 +528,7 @@
    * First check validity and concatenate parts of the query.
    */
   va_start (ap, stmt);
-  sql = [[db _prepare: stmt args: ap] objectAtIndex: 0];
+  sql = [[db prepare: stmt args: ap] objectAtIndex: 0];
   va_end (ap);
   [self swallowClient: db];
 
@@ -556,7 +552,7 @@
   va_list              ap;
 
   va_start (ap, stmt);
-  stmt = [[db _prepare: stmt args: ap] objectAtIndex: 0];
+  stmt = [[db prepare: stmt args: ap] objectAtIndex: 0];
   va_end (ap);
   result = [db cache: seconds simpleQuery: stmt];
   [self swallowClient: db];
@@ -612,7 +608,7 @@
   va_list      ap;
 
   va_start (ap, stmt);
-  info = [db _prepare: stmt args: ap];
+  info = [db prepare: stmt args: ap];
   va_end (ap);
   result = [db simpleExecute: info];
   [self swallowClient: db];
@@ -638,7 +634,7 @@
    * First check validity and concatenate parts of the query.
    */
   va_start (ap, stmt);
-  stmt = [[db _prepare: stmt args: ap] objectAtIndex: 0];
+  stmt = [[db prepare: stmt args: ap] objectAtIndex: 0];
   va_end (ap);
   result = [db simpleQuery: stmt];
   [self swallowClient: db];
@@ -663,7 +659,7 @@
   va_list      ap;
 
   va_start (ap, stmt);
-  stmt = [[db _prepare: stmt args: ap] objectAtIndex: 0];
+  stmt = [[db prepare: stmt args: ap] objectAtIndex: 0];
   va_end (ap);
   result = [db simpleQuery: stmt];
   [self swallowClient: db];
@@ -690,7 +686,7 @@
   va_list      ap;
 
   va_start (ap, stmt);
-  stmt = [[db _prepare: stmt args: ap] objectAtIndex: 0];
+  stmt = [[db prepare: stmt args: ap] objectAtIndex: 0];
   va_end (ap);
   result = [db simpleQuery: stmt];
   [self swallowClient: db];

Modified: libs/sqlclient/trunk/testPostgres.m
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/sqlclient/trunk/testPostgres.m?rev=38400&r1=38399&r2=38400&view=diff
==============================================================================
--- libs/sqlclient/trunk/testPostgres.m (original)
+++ libs/sqlclient/trunk/testPostgres.m Wed Mar 11 18:16:14 2015
@@ -120,7 +120,7 @@
            @"Delivery TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, "
            @"Reference CHAR(128), "
            @"Destination CHAR(15) NOT NULL, "
-           @"Payload CHAR(250) DEFAULT '' NOT NULL"
+           @"Payload CHAR(250) DEFAULT '' NOT NULL,"
            @")",
            nil];
          [db execute:
@@ -155,8 +155,11 @@
             }
          [db execute: @"INSERT INTO Queue (Consumer, Destination,"
             @" ServiceID, Payload) VALUES (",
-           [db quote: name], @", ", [db quote: destination], @", ", sid, @", ",
-           @"'helo there'", @")", nil];
+           [db quote: name], @", ",
+            [db quote: destination], @", ",
+            sid, @", ",
+           @"'helo there'",
+            @")", nil];
          [arp release];
        }
       NSLog(@"End producing");
@@ -252,25 +255,54 @@
        @"intval int, "
        @"when1 timestamp with time zone, "
        @"when2 timestamp, "
-       @"b bytea"
+       @"b bytea,"
+        @"extra1 int[],"
+        @"extra2 varchar[],"
+        @"extra3 bytea[],"
+        @"extra4 boolean[],"
+        @"extra5 timestamp[]"
        @")",
        nil];
 
-      if (1 != [db execute: @"insert into xxx "
-       @"(k, char1, boolval, intval, when1, when2, b) "
+      if (1 != [db execute: @"insert into xxx (k, char1, boolval, intval,"
+        @" when1, when2, b, extra1, extra2, extra3, extra4, extra5) "
        @"values ("
-       @"'hello', "
+       @"'{hello', "
        @"'X', "
        @"TRUE, "
        @"1, "
        @"CURRENT_TIMESTAMP, "
        @"CURRENT_TIMESTAMP, ",
-       data,
-       @")",
+       data, @", ",
+        [db quoteArray:
+          [NSArray arrayWithObjects: @"1", @"2", [NSNull null], nil]
+              toString: nil
+        quotingStrings: NO], @", ",
+        [db quoteArray:
+          [NSArray arrayWithObjects: @"on,e", @"t'wo", @"many", nil]
+              toString: nil
+        quotingStrings: YES], @", ",
+        [db quoteArray:
+          [NSArray arrayWithObjects: data, nil]
+              toString: nil
+        quotingStrings: YES], @", ",
+        [db quoteArray:
+          [NSArray arrayWithObjects: @"TRUE", @"FALSE", nil]
+              toString: nil
+        quotingStrings: NO], @", ",
+        [db quoteArray:
+          [NSArray arrayWithObjects: [NSDate date], nil]
+              toString: nil
+        quotingStrings: YES], @")",
        nil])
         {
           NSLog(@"Insert failed to return row count");
         }
+
+[db setDebugging: 9];
+[db query: @"select * from xxx", nil];
+[db setDebugging: 0];
+
       [db execute: @"insert into xxx "
        @"(k, char1, boolval, intval, when1, when2, b) "
        @"values ("


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

Reply via email to