mirror of
https://github.com/gnustep/libs-sqlclient.git
synced 2025-02-14 15:40:59 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/sqlclient/trunk@40494 72102866-910b-0410-8b05-ffd578937521
518 lines
15 KiB
Objective-C
518 lines
15 KiB
Objective-C
/**
|
|
Copyright (C) 2004 Free Software Foundation, Inc.
|
|
|
|
Written by: Richard Frith-Macdonald <rfm@gnu.org>
|
|
Date: April 2004
|
|
|
|
This file is part of the SQLClient Library.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 3 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free
|
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
|
|
|
$Date$ $Revision$
|
|
*/
|
|
|
|
#import <Foundation/Foundation.h>
|
|
#import <Performance/GSCache.h>
|
|
#import "SQLClient.h"
|
|
|
|
@interface Logger : NSObject
|
|
- (void) notified: (NSNotification*)n;
|
|
@end
|
|
|
|
@implementation Logger
|
|
- (void) notified: (NSNotification*)n
|
|
{
|
|
NSLog(@"Received %@", n);
|
|
}
|
|
@end
|
|
|
|
int
|
|
main()
|
|
{
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
SQLClientPool *sp;
|
|
SQLClient *db;
|
|
NSUserDefaults *defs;
|
|
NSMutableArray *records;
|
|
SQLRecord *record;
|
|
unsigned char dbuf[256];
|
|
unsigned int i;
|
|
NSData *data;
|
|
NSString *name;
|
|
Logger *l;
|
|
|
|
defs = [NSUserDefaults standardUserDefaults];
|
|
[defs registerDefaults:
|
|
[NSDictionary dictionaryWithObjectsAndKeys:
|
|
[NSDictionary dictionaryWithObjectsAndKeys:
|
|
[NSDictionary dictionaryWithObjectsAndKeys:
|
|
@"template1@localhost", @"Database",
|
|
@"postgres", @"User",
|
|
@"postgres", @"Password",
|
|
@"Postgres", @"ServerType",
|
|
nil],
|
|
@"test",
|
|
nil],
|
|
@"SQLClientReferences",
|
|
nil]
|
|
];
|
|
|
|
sp = [[[SQLClientPool alloc] initWithConfiguration: nil
|
|
name: @"test"
|
|
max: 2
|
|
min: 1] autorelease];
|
|
#if 0
|
|
{
|
|
NSAutoreleasePool *p;
|
|
NSAutoreleasePool *q;
|
|
SQLClient *c0;
|
|
SQLClient *c1;
|
|
|
|
[sp setDebugging: 4];
|
|
p = [NSAutoreleasePool new];
|
|
c0 = [sp provideClientExclusive];
|
|
c1 = [sp provideClientExclusive];
|
|
NSLog(@"Got two clients from pool");
|
|
[c0 connect];
|
|
[c1 connect];
|
|
NSLog(@"Now putting clients back in pool again");
|
|
[sp swallowClient: c0];
|
|
[sp swallowClient: c1];
|
|
NSLog(@"And emptying autorelease pool");
|
|
[p release];
|
|
p = [NSAutoreleasePool new];
|
|
[sp setPurgeAll: 60 min: 1];
|
|
[NSThread sleepForTimeInterval: 1.0];
|
|
NSLog(@"Expecting purge to disconnect one client");
|
|
[sp purge];
|
|
c0 = [sp provideClientExclusive];
|
|
c1 = [sp provideClientExclusive];
|
|
NSLog(@"Expecting connected: %@", [c0 connected] ? @"YES" : @"NO");
|
|
NSLog(@"Expecting not connected: %@", [c1 connected] ? @"NO" : @"YES");
|
|
|
|
NSLog(@"Pool has provided both it's clients ... now try for another with a 15 second timeout");
|
|
[sp provideClientBeforeDate: [NSDate dateWithTimeIntervalSinceNow: 15.0]
|
|
exclusive: YES];
|
|
NSLog(@"Emptying autorelease pool ... clients should be put back in pool");
|
|
[p release];
|
|
p = [NSAutoreleasePool new];
|
|
NSLog(@"Getting two clients again");
|
|
c0 = [sp provideClientExclusive];
|
|
c1 = [sp provideClientExclusive];
|
|
NSLog(@"Pool has provided both it's clients ... now try for another with a 15 second timeout");
|
|
[sp provideClientBeforeDate: [NSDate dateWithTimeIntervalSinceNow: 15.0]
|
|
exclusive: YES];
|
|
NSLog(@"Emptying autorelease pool again");
|
|
[p release];
|
|
p = [NSAutoreleasePool new];
|
|
c0 = [sp provideClient];
|
|
q = [NSAutoreleasePool new];
|
|
c1 = [sp provideClient];
|
|
if (c0 != c1)
|
|
{
|
|
NSLog(@"ERROR was expecting provideClient to give the same object");
|
|
exit(1);
|
|
}
|
|
[q release];
|
|
[c0 connect];
|
|
[p release];
|
|
|
|
NSLog(@"Expect to get client immediately");
|
|
}
|
|
#endif
|
|
db = [sp provideClientExclusive];
|
|
[sp swallowClient: db];
|
|
|
|
[sp queryString: @"SELECT CURRENT_TIMESTAMP", nil];
|
|
db = [sp provideClientExclusive];
|
|
|
|
l = [Logger new];
|
|
[[NSNotificationCenter defaultCenter] addObserver: l
|
|
selector: @selector(notified:)
|
|
name: SQLClientDidConnectNotification
|
|
object: db];
|
|
[[NSNotificationCenter defaultCenter] addObserver: l
|
|
selector: @selector(notified:)
|
|
name: SQLClientDidDisconnectNotification
|
|
object: db];
|
|
|
|
if ((name = [defs stringForKey: @"Producer"]) != nil)
|
|
{
|
|
NS_DURING
|
|
{
|
|
[db execute: @"CREATE TABLE Queue ( "
|
|
@"ID SERIAL, "
|
|
@"Consumer CHAR(40) NOT NULL, "
|
|
@"ServiceID INT NOT NULL, "
|
|
@"Status CHAR(1) DEFAULT 'Q' NOT NULL, "
|
|
@"Delivery TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, "
|
|
@"Reference CHAR(128), "
|
|
@"Destination CHAR(15) NOT NULL, "
|
|
@"Payload CHAR(250) DEFAULT '' NOT NULL,"
|
|
@")",
|
|
nil];
|
|
[db execute:
|
|
@"CREATE UNIQUE INDEX QueueIDX ON Queue (ID)",
|
|
nil];
|
|
[db execute:
|
|
@"CREATE INDEX ServiceIDX ON Queue (ServiceID)",
|
|
nil];
|
|
[db execute:
|
|
@"CREATE INDEX ConsumerIDX ON Queue (Consumer,Status,Delivery)",
|
|
nil];
|
|
[db execute:
|
|
@"CREATE INDEX ReferenceIDX ON Queue (Reference,Consumer)",
|
|
nil];
|
|
}
|
|
NS_HANDLER
|
|
{
|
|
NSLog(@"%@", localException);
|
|
}
|
|
NS_ENDHANDLER
|
|
NSLog(@"Start producing");
|
|
for (i = 0; i < 100000; i++)
|
|
{
|
|
NSAutoreleasePool *arp = [NSAutoreleasePool new];
|
|
NSString *destination = [NSString stringWithFormat: @"%d", i];
|
|
NSString *sid = [NSString stringWithFormat: @"%d", i%100];
|
|
|
|
if (i % 1000 == 999)
|
|
{
|
|
[db postNotificationName: @"Producing"
|
|
payload: [NSString stringWithFormat: @"%d", i]];
|
|
}
|
|
[db execute: @"INSERT INTO Queue (Consumer, Destination,"
|
|
@" ServiceID, Payload) VALUES (",
|
|
[db quote: name], @", ",
|
|
[db quote: destination], @", ",
|
|
sid, @", ",
|
|
@"'helo there'",
|
|
@")", nil];
|
|
[arp release];
|
|
}
|
|
NSLog(@"End producing");
|
|
}
|
|
else if ((name = [defs stringForKey: @"Consumer"]) != nil)
|
|
{
|
|
[db addObserver: l
|
|
selector: @selector(notified:)
|
|
name: @"Producing"];
|
|
NSLog(@"Start consuming");
|
|
for (i = 0; i < 100000;)
|
|
{
|
|
NSAutoreleasePool *arp = [NSAutoreleasePool new];
|
|
unsigned count;
|
|
int j;
|
|
|
|
[db begin];
|
|
records = [db query: @"SELECT * FROM Queue WHERE Consumer = ",
|
|
[db quote: name],
|
|
@" AND Status = 'Q' AND Delivery < CURRENT_TIMESTAMP",
|
|
@" ORDER BY Delivery LIMIT 1000 FOR UPDATE" , nil];
|
|
count = [records count];
|
|
if (count == 0)
|
|
{
|
|
[db commit];
|
|
[NSThread sleepForTimeInterval: 1.0];
|
|
[db begin];
|
|
records = [db query: @"SELECT * FROM Queue WHERE Consumer = ",
|
|
[db quote: name],
|
|
@" AND Status = 'Q' AND Delivery < CURRENT_TIMESTAMP",
|
|
@" ORDER BY Delivery LIMIT 50 FOR UPDATE" , nil];
|
|
count = [records count];
|
|
if (count == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < count; j++)
|
|
{
|
|
SQLRecord *record = [records objectAtIndex: j];
|
|
NSString *reference = [record objectForKey: @"ID"];
|
|
|
|
[db execute: @"UPDATE Queue SET Status = 'S', Reference = ",
|
|
[db quote: reference], @" WHERE ID = ",
|
|
[record objectForKey: @"ID"], nil];
|
|
|
|
[db execute: @"UPDATE Queue SET Status = 'D'",
|
|
@" WHERE Consumer = ", [db quote: name],
|
|
@" AND Reference = ", [db quote: reference],
|
|
nil];
|
|
}
|
|
[db commit];
|
|
i += count;
|
|
[arp release];
|
|
}
|
|
NSLog(@"End consuming (%d records)", i);
|
|
/*
|
|
[db execute: @"DROP INDEX ReferenceIDX", nil];
|
|
[db execute: @"DROP INDEX ServiceIDX", nil];
|
|
[db execute: @"DROP INDEX ConsumerIDX", nil];
|
|
[db execute: @"DROP INDEX QueueIDX", nil];
|
|
[db execute: @"DROP TABLE Queue", nil];
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
NSString *oddChars;
|
|
NSString *oddNoNul;
|
|
NSString *nonLatin;
|
|
id e1, e2, e3, e4, e5;
|
|
id d;
|
|
id d0;
|
|
id d1;
|
|
id d2;
|
|
id r0;
|
|
id r1;
|
|
|
|
oddChars = @"'a\\b'c\0\r\nd'\\ed\\";
|
|
oddNoNul = @"'a\\b'c\r\nd'\\ed\\";
|
|
nonLatin = [[NSString stringWithCString: "\"\\U2A11\""] propertyList];
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
dbuf[i] = i;
|
|
}
|
|
data = [NSData dataWithBytes: dbuf length: i];
|
|
|
|
NS_DURING
|
|
[db execute: @"drop table xxx", nil];
|
|
NS_HANDLER
|
|
NS_ENDHANDLER
|
|
|
|
[db setDurationLogging: 0];
|
|
|
|
[db begin];
|
|
[db execute: @"create table xxx ( "
|
|
@"id int, "
|
|
@"k char(40), "
|
|
@"char1 char(1), "
|
|
@"boolval BOOL, "
|
|
@"intval int, "
|
|
@"when1 timestamp with time zone, "
|
|
@"when2 timestamp, "
|
|
@"b bytea,"
|
|
@"extra1 int[],"
|
|
@"extra2 varchar[],"
|
|
@"extra3 bytea[],"
|
|
@"extra4 boolean[],"
|
|
@"extra5 timestamp with time zone[]"
|
|
@")",
|
|
nil];
|
|
|
|
if (1 != [db execute: @"INSERT into xxx (id, k, char1, boolval, intval,"
|
|
@" when1, when2, b, extra1, extra2, extra3, extra4, extra5) "
|
|
@"values (1,"
|
|
@"'{hello', "
|
|
@"'X', "
|
|
@"TRUE, "
|
|
@"1, "
|
|
@"CURRENT_TIMESTAMP, "
|
|
@"CURRENT_TIMESTAMP, ",
|
|
data, @", ",
|
|
[db quoteArray:
|
|
(e1 = [NSArray arrayWithObjects: @"1", @"2", [NSNull null], nil])
|
|
toString: nil
|
|
quotingStrings: NO], @", ",
|
|
[db quoteArray:
|
|
(e2 = [NSArray arrayWithObjects: @"on,e", @"t'wo", @"many", nil])
|
|
toString: nil
|
|
quotingStrings: YES], @", ",
|
|
[db quoteArray:
|
|
(e3 = [NSArray arrayWithObjects: data, nil])
|
|
toString: nil
|
|
quotingStrings: YES], @", ",
|
|
[db quoteArray:
|
|
(e4 =[NSArray arrayWithObjects: @"TRUE", @"FALSE", nil])
|
|
toString: nil
|
|
quotingStrings: NO], @", ",
|
|
[db quoteArray:
|
|
(e5 = [NSArray arrayWithObjects: [NSDate date], nil])
|
|
toString: nil
|
|
quotingStrings: YES], @")",
|
|
nil])
|
|
{
|
|
NSLog(@"Insert failed to return row count");
|
|
}
|
|
|
|
[db setDebugging: 0];
|
|
[db query: @"select * from xxx", nil];
|
|
[db setDebugging: 0];
|
|
|
|
[db execute: @"INSERT into xxx "
|
|
@"(id, k, char1, boolval, intval, when1, when2, b) "
|
|
@"values (2,"
|
|
@"'hello', "
|
|
@"'X', "
|
|
@"TRUE, "
|
|
@"1, ",
|
|
[NSDate date], @", ",
|
|
[NSDate date], @", ",
|
|
[NSData dataWithBytes: "" length: 0],
|
|
@")",
|
|
nil];
|
|
[db execute: @"INSERT into xxx "
|
|
@"(id, k, char1, boolval, intval, when1, when2, b) "
|
|
@"values (3,",
|
|
[db quote: oddChars],
|
|
@", ",
|
|
[db quote: nonLatin],
|
|
@",TRUE, "
|
|
@"1, ",
|
|
[NSDate date], @", ",
|
|
[NSDate date], @", ",
|
|
[NSData dataWithBytes: "" length: 0],
|
|
@")",
|
|
nil];
|
|
[db commit];
|
|
|
|
r0 = [db cache: 1 query: @"select * from xxx order by id", nil];
|
|
r1 = [db cache: 1 query: @"select * from xxx order by id", nil];
|
|
NSCAssert([r0 lastObject] == [r1 lastObject], @"Cache failed");
|
|
[NSThread sleepForTimeInterval: 2.0];
|
|
records = [db cache: 1 query: @"select * from xxx order by id", nil];
|
|
NSCAssert([r0 lastObject] != [records lastObject], @"Lifetime failed");
|
|
|
|
db = [[[SQLClient alloc] initWithConfiguration: nil
|
|
name: @"test"] autorelease];
|
|
[db addObserver: l
|
|
selector: @selector(notified:)
|
|
name: @"foo"];
|
|
|
|
[db postNotificationName: @"foo" payload: @"hello"];
|
|
|
|
[db execute: @"drop table xxx", nil];
|
|
|
|
if ([records count] != 3)
|
|
{
|
|
NSLog(@"Expected 3 records but got %lu", [records count]);
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
|
|
record = [records objectAtIndex: 0];
|
|
if ([[record objectForKey: @"b"] isEqual: data] == NO)
|
|
{
|
|
NSLog(@"Retrieved data does not match saved data %@ %@",
|
|
data, [record objectForKey: @"b"]);
|
|
}
|
|
record = [records objectAtIndex: 1];
|
|
if ([[record objectForKey: @"b"] isEqual: [NSData data]] == NO)
|
|
{
|
|
NSLog(@"Retrieved empty data does not match saved data");
|
|
}
|
|
record = [records objectAtIndex: 2];
|
|
if ([[record objectForKey: @"char1"] isEqual: nonLatin] == NO)
|
|
{
|
|
NSLog(@"Retrieved non-latin does not match saved string");
|
|
}
|
|
id o = [[record objectForKey: @"k"] stringByTrimmingSpaces];
|
|
if ([o isEqual: oddNoNul] == NO)
|
|
{
|
|
NSLog(@"Retrieved odd chars (%@) does not match oddNoNul (%@)",
|
|
o, oddNoNul);
|
|
}
|
|
else
|
|
{
|
|
NSLog(@"Embedded nul correctly removed");
|
|
}
|
|
record = [records objectAtIndex: 0];
|
|
o = [record objectForKey: @"extra1"];
|
|
if ([o isEqual: e1] == NO)
|
|
{
|
|
NSLog(@"Retrieved extra1 (%@) does not match saved (%@)", o, e1);
|
|
}
|
|
o = [record objectForKey: @"extra2"];
|
|
if ([o isEqual: e2] == NO)
|
|
{
|
|
NSLog(@"Retrieved extra2 (%@) does not match saved (%@)", o, e2);
|
|
}
|
|
o = [record objectForKey: @"extra3"];
|
|
if ([o isEqual: e3] == NO)
|
|
{
|
|
NSLog(@"Retrieved extra3 (%@) does not match saved (%@)", o, e3);
|
|
}
|
|
o = [record objectForKey: @"extra4"];
|
|
if ([o count] != [e4 count])
|
|
{
|
|
NSLog(@"Retrieved extra4 (%@) does not match saved (%@)", o, e4);
|
|
}
|
|
for (i = 0; i < [o count]; i++)
|
|
{
|
|
if ([[o objectAtIndex: i] boolValue]
|
|
!= [[e4 objectAtIndex: i] boolValue])
|
|
{
|
|
NSLog(@"Retrieved extra4 (%@) does not match saved (%@)",
|
|
o, e4);
|
|
}
|
|
}
|
|
o = [record objectForKey: @"extra5"];
|
|
if ([o count] != [e5 count])
|
|
{
|
|
NSLog(@"Retrieved extra5 (%@) does not match saved (%@)", o, e5);
|
|
}
|
|
for (i = 0; i < [o count]; i++)
|
|
{
|
|
if (floor([[o objectAtIndex: i] timeIntervalSinceReferenceDate])
|
|
!= floor([[e5 objectAtIndex: i] timeIntervalSinceReferenceDate]))
|
|
{
|
|
NSLog(@"Retrieved extra5 (%@) does not match saved (%@)",
|
|
o, e5);
|
|
}
|
|
}
|
|
}
|
|
|
|
NSLog(@"Records - %@", [GSCache class]);
|
|
|
|
d = [NSCalendarDate date];
|
|
[d setTimeZone: [NSTimeZone timeZoneForSecondsFromGMT: 240]];
|
|
[db begin];
|
|
[db execute: @"create table xxx ( "
|
|
@"id int, "
|
|
@"when0 timestamp with time zone, "
|
|
@"when1 timestamp with time zone, "
|
|
@"when2 timestamp)",
|
|
nil];
|
|
[db execute: @"INSERT into xxx (id, when0, when1, when2) "
|
|
@"values (99,",
|
|
d, @", ",
|
|
[NSDate distantPast], @", ",
|
|
[NSDate distantFuture], @")",
|
|
nil];
|
|
r0 = [[db query: @"select * from xxx where id=99", nil] lastObject];
|
|
[db execute: @"drop table xxx", nil];
|
|
[db commit];
|
|
|
|
d0 = [r0 objectForKey:@"when0"];
|
|
NSCAssert(floor([d0 timeIntervalSinceReferenceDate])
|
|
== floor([d timeIntervalSinceReferenceDate]),
|
|
NSInternalInconsistencyException);
|
|
d1 = [r0 objectForKey:@"when1"];
|
|
NSCAssert([d1 timeIntervalSinceReferenceDate]
|
|
== [[NSDate distantPast] timeIntervalSinceReferenceDate],
|
|
NSInternalInconsistencyException);
|
|
d2 = [r0 objectForKey:@"when2"];
|
|
NSCAssert([d2 timeIntervalSinceReferenceDate]
|
|
== [[NSDate distantFuture] timeIntervalSinceReferenceDate],
|
|
NSInternalInconsistencyException);
|
|
}
|
|
|
|
NSLog(@"Pool stats:\n%@", [sp statistics]);
|
|
|
|
[pool release];
|
|
return 0;
|
|
}
|
|
|