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