mirror of
https://github.com/gnustep/libs-sqlclient.git
synced 2025-02-19 01:50:49 +00:00
SQLRecord enhancements for KVC and for performance optimisations.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/sqlclient/trunk@24827 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
dd2ac4d9e6
commit
420fe259a8
9 changed files with 465 additions and 52 deletions
17
ChangeLog
17
ChangeLog
|
@ -1,3 +1,20 @@
|
||||||
|
2007-03-08 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
|
* SQLClient.h:
|
||||||
|
* SQLClient.m:
|
||||||
|
* MySQL.m:
|
||||||
|
* ECPG.pgm:
|
||||||
|
* Postgres.m:
|
||||||
|
* Oracle.pm:
|
||||||
|
* SQLite.m:
|
||||||
|
* JDBC.m:
|
||||||
|
Add KVC support for SQLRecord. Make SQLRecord into a class cluster
|
||||||
|
with a single concrete implementation for now. Extend API to allow
|
||||||
|
specifying of an alternative SQLRecord subclass when doing a query
|
||||||
|
so that query results can be efficiently stored into custom subclasses
|
||||||
|
rather than having to first be retrieved into an SQLRecord and then
|
||||||
|
copied.
|
||||||
|
|
||||||
2007-02-14 Nicola Pero <nicola.pero@meta-innovation.com>
|
2007-02-14 Nicola Pero <nicola.pero@meta-innovation.com>
|
||||||
|
|
||||||
* GNUmakefile (BUNDLE_INSTALL_DIR): Set using GNUSTEP_BUNDLES,
|
* GNUmakefile (BUNDLE_INSTALL_DIR): Set using GNUSTEP_BUNDLES,
|
||||||
|
|
8
ECPG.pgm
8
ECPG.pgm
|
@ -355,7 +355,7 @@ static unsigned int trim(char *str)
|
||||||
return (str - start);
|
return (str - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSMutableArray*) backendQuery: (NSString*)stmt
|
- (NSMutableArray*) backendQuery: (NSString*)stmt recordClass: (Class)rClass
|
||||||
{
|
{
|
||||||
EXEC SQL BEGIN DECLARE SECTION;
|
EXEC SQL BEGIN DECLARE SECTION;
|
||||||
bool aBool;
|
bool aBool;
|
||||||
|
@ -597,9 +597,9 @@ static unsigned int trim(char *str)
|
||||||
keys[index-1] = [NSString stringWithUTF8String:
|
keys[index-1] = [NSString stringWithUTF8String:
|
||||||
fieldName];
|
fieldName];
|
||||||
}
|
}
|
||||||
record = [SQLRecord newWithValues: values
|
record = [rClass newWithValues: values
|
||||||
keys: keys
|
keys: keys
|
||||||
count: count];
|
count: count];
|
||||||
[records addObject: record];
|
[records addObject: record];
|
||||||
RELEASE(record);
|
RELEASE(record);
|
||||||
}
|
}
|
||||||
|
|
8
JDBC.m
8
JDBC.m
|
@ -1241,7 +1241,7 @@ static int JDBCVARCHAR = 0;
|
||||||
DESTROY(arp);
|
DESTROY(arp);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSMutableArray*) backendQuery: (NSString*)stmt
|
- (NSMutableArray*) backendQuery: (NSString*)stmt recordClass: (Class)rClass
|
||||||
{
|
{
|
||||||
NSMutableArray *records = nil;
|
NSMutableArray *records = nil;
|
||||||
CREATE_AUTORELEASE_POOL(arp);
|
CREATE_AUTORELEASE_POOL(arp);
|
||||||
|
@ -1474,9 +1474,9 @@ static int JDBCVARCHAR = 0;
|
||||||
[localException raise];
|
[localException raise];
|
||||||
}
|
}
|
||||||
NS_ENDHANDLER
|
NS_ENDHANDLER
|
||||||
record = [SQLRecord newWithValues: values
|
record = [rClass newWithValues: values
|
||||||
keys: keys
|
keys: keys
|
||||||
count: fieldCount];
|
count: fieldCount];
|
||||||
[records addObject: record];
|
[records addObject: record];
|
||||||
RELEASE(record);
|
RELEASE(record);
|
||||||
}
|
}
|
||||||
|
|
8
MySQL.m
8
MySQL.m
|
@ -266,7 +266,7 @@ static unsigned int trim(char *str)
|
||||||
return (str - start);
|
return (str - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSMutableArray*) backendQuery: (NSString*)stmt
|
- (NSMutableArray*) backendQuery: (NSString*)stmt recordClass: (Class)rClass
|
||||||
{
|
{
|
||||||
CREATE_AUTORELEASE_POOL(arp);
|
CREATE_AUTORELEASE_POOL(arp);
|
||||||
NSMutableArray *records = nil;
|
NSMutableArray *records = nil;
|
||||||
|
@ -412,9 +412,9 @@ static unsigned int trim(char *str)
|
||||||
}
|
}
|
||||||
values[j] = v;
|
values[j] = v;
|
||||||
}
|
}
|
||||||
record = [SQLRecord newWithValues: values
|
record = [rClass newWithValues: values
|
||||||
keys: keys
|
keys: keys
|
||||||
count: fieldCount];
|
count: fieldCount];
|
||||||
[records addObject: record];
|
[records addObject: record];
|
||||||
RELEASE(record);
|
RELEASE(record);
|
||||||
}
|
}
|
||||||
|
|
|
@ -326,7 +326,7 @@ static unsigned int trim(char *str)
|
||||||
return (str - start);
|
return (str - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSMutableArray*) backendQuery: (NSString*)stmt
|
- (NSMutableArray*) backendQuery: (NSString*)stmt recordClass: (Class)rClass
|
||||||
{
|
{
|
||||||
EXEC SQL BEGIN DECLARE SECTION;
|
EXEC SQL BEGIN DECLARE SECTION;
|
||||||
int count;
|
int count;
|
||||||
|
@ -597,9 +597,9 @@ static unsigned int trim(char *str)
|
||||||
keys[index - 1] = [NSString stringWithUTF8String:
|
keys[index - 1] = [NSString stringWithUTF8String:
|
||||||
fieldName];
|
fieldName];
|
||||||
}
|
}
|
||||||
record = [SQLRecord newWithValues: values
|
record = [rClass newWithValues: values
|
||||||
keys: keys
|
keys: keys
|
||||||
count: count];
|
count: count];
|
||||||
[records addObject: record];
|
[records addObject: record];
|
||||||
RELEASE(record);
|
RELEASE(record);
|
||||||
}
|
}
|
||||||
|
|
|
@ -363,7 +363,7 @@ static unsigned int trim(char *str)
|
||||||
return (str - start);
|
return (str - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSMutableArray*) backendQuery: (NSString*)stmt
|
- (NSMutableArray*) backendQuery: (NSString*)stmt recordClass: (Class)rClass
|
||||||
{
|
{
|
||||||
CREATE_AUTORELEASE_POOL(arp);
|
CREATE_AUTORELEASE_POOL(arp);
|
||||||
PGresult *result = 0;
|
PGresult *result = 0;
|
||||||
|
@ -490,9 +490,9 @@ static unsigned int trim(char *str)
|
||||||
}
|
}
|
||||||
values[j] = v;
|
values[j] = v;
|
||||||
}
|
}
|
||||||
record = [SQLRecord newWithValues: values
|
record = [rClass newWithValues: values
|
||||||
keys: keys
|
keys: keys
|
||||||
count: fieldCount];
|
count: fieldCount];
|
||||||
[records addObject: record];
|
[records addObject: record];
|
||||||
RELEASE(record);
|
RELEASE(record);
|
||||||
}
|
}
|
||||||
|
|
127
SQLClient.h
127
SQLClient.h
|
@ -188,15 +188,17 @@
|
||||||
@class SQLTransaction;
|
@class SQLTransaction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An enhanced array to represent a record returned from a query.
|
* <p>An enhanced array to represent a record returned from a query.
|
||||||
* You should <em>NOT</em> try to create instances of this class
|
* You should <em>NOT</em> try to create instances of this class
|
||||||
* except via the +newWithValues:keys:count: method.
|
* except via the +newWithValues:keys:count: method.
|
||||||
|
* </p>
|
||||||
|
* <p>NB. SQLRecord is the abstract base class of a class cluster.
|
||||||
|
* If you wish to subclass it you must implement the primitive methods
|
||||||
|
* +newWithValues:keys:count: -count -keyAtIndex: -objectAtIndex:
|
||||||
|
* and -replaceObjectAtIndex:withObject:
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
@interface SQLRecord : NSArray
|
@interface SQLRecord : NSArray
|
||||||
{
|
|
||||||
@private
|
|
||||||
unsigned int count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new SQLRecord containing the specified fields.<br />
|
* Create a new SQLRecord containing the specified fields.<br />
|
||||||
|
@ -212,23 +214,50 @@
|
||||||
*/
|
*/
|
||||||
- (NSArray*) allKeys;
|
- (NSArray*) allKeys;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of items in the record.<br />
|
||||||
|
* Subclasses must implement this method.
|
||||||
|
*/
|
||||||
|
- (unsigned) count;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the record as a mutable dictionary with the keys as the
|
* Return the record as a mutable dictionary with the keys as the
|
||||||
* record field names standardised to be lowercase strings.
|
* record field names standardised to be lowercase strings.
|
||||||
*/
|
*/
|
||||||
- (NSMutableDictionary*) dictionary;
|
- (NSMutableDictionary*) dictionary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optimised mechanism for retrieving all keys in order.
|
||||||
|
*/
|
||||||
|
- (void) getKeys: (id*)buf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optimised mechanism for retrieving all objects.
|
* Optimised mechanism for retrieving all objects.
|
||||||
*/
|
*/
|
||||||
- (void) getObjects: (id*)buf;
|
- (void) getObjects: (id*)buf;
|
||||||
|
|
||||||
|
/** <override-subclass />
|
||||||
|
* Returns the key at the specified indes.<br />
|
||||||
|
*/
|
||||||
|
- (NSString*) keyAtIndex: (unsigned)index;
|
||||||
|
|
||||||
|
/** <override-subclass />
|
||||||
|
* Returns the object at the specified indes.<br />
|
||||||
|
*/
|
||||||
|
- (id) objectAtIndex: (unsigned)index;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the named field.<br />
|
* Returns the value of the named field.<br />
|
||||||
* The field name is case insensitive.
|
* The field name is case insensitive.
|
||||||
*/
|
*/
|
||||||
- (id) objectForKey: (NSString*)key;
|
- (id) objectForKey: (NSString*)key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the value at the specified index.<br />
|
||||||
|
* Subclasses must implement this method.
|
||||||
|
*/
|
||||||
|
- (void) replaceObjectAtIndex: (unsigned)index withObject: (id)anObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces the value of the named field.<br />
|
* Replaces the value of the named field.<br />
|
||||||
* The field name is case insensitive.<br />
|
* The field name is case insensitive.<br />
|
||||||
|
@ -400,6 +429,49 @@ extern unsigned SQLClientTimeTick();
|
||||||
*/
|
*/
|
||||||
- (void) begin;
|
- (void) begin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Build an sql query string using the supplied arguments.
|
||||||
|
* </p>
|
||||||
|
* <p>This method has at least one argument, the string starting the
|
||||||
|
* query to be executed (which must have the prefix 'select ').
|
||||||
|
* </p>
|
||||||
|
* <p>Additional arguments are a nil terminated list which also be strings,
|
||||||
|
* and these are appended to the statement.<br />
|
||||||
|
* Any string arguments are assumed to have been quoted appropriately
|
||||||
|
* already, but non-string arguments are automatically quoted using the
|
||||||
|
* -quote: method.
|
||||||
|
* </p>
|
||||||
|
* <example>
|
||||||
|
* sql = [db buildQuery: @"SELECT Name FROM ", table, nil];
|
||||||
|
* </example>
|
||||||
|
* <p>Upon error, an exception is raised.
|
||||||
|
* </p>
|
||||||
|
* <p>The method returns a string containing sql suitable for passing to
|
||||||
|
* the -simpleQuery:recordClass: or -cache:simpleQuery:recordClass: methods.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
- (NSString*) buildQuery: (NSString*)stmt,...;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes the query statement and substitutes in values from
|
||||||
|
* the dictionary where markup of the format {key} is found.<br />
|
||||||
|
* Returns the resulting query string.
|
||||||
|
* <example>
|
||||||
|
* sql = [db buildQuery: @"SELECT Name FROM {Table} WHERE ID = {ID}"
|
||||||
|
* with: values];
|
||||||
|
* </example>
|
||||||
|
* <p>Any non-string values in the dictionary will be replaced by
|
||||||
|
* the results of the -quote: method.<br />
|
||||||
|
* The markup format may also be {key?default} where <em>default</em>
|
||||||
|
* is a string to be used if there is no value for the <em>key</em>
|
||||||
|
* in the dictionary.
|
||||||
|
* </p>
|
||||||
|
* <p>The method returns a string containing sql suitable for passing to
|
||||||
|
* the -simpleQuery:recordClass: or -cache:simpleQuery:recordClass: methods.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
- (NSString*) buildQuery: (NSString*)stmt with: (NSDictionary*)values;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the client name for this instance.<br />
|
* Return the client name for this instance.<br />
|
||||||
* Normally this is useful only for debugging/reporting purposes, but
|
* Normally this is useful only for debugging/reporting purposes, but
|
||||||
|
@ -548,14 +620,8 @@ extern unsigned SQLClientTimeTick();
|
||||||
/**
|
/**
|
||||||
* <p>Perform arbitrary query <em>which returns values.</em>
|
* <p>Perform arbitrary query <em>which returns values.</em>
|
||||||
* </p>
|
* </p>
|
||||||
* <p>This method has at least one argument, the string starting the
|
* <p>This method handles its arguments in the same way as the -buildQuery:,...
|
||||||
* statement to be executed (which must have the prefix 'select ').
|
* method and returns the result of the query.
|
||||||
* </p>
|
|
||||||
* <p>Additional arguments are a nil terminated list which also be strings,
|
|
||||||
* and these are appended to the statement.<br />
|
|
||||||
* Any string arguments are assumed to have been quoted appropriately
|
|
||||||
* already, but non-string arguments are automatically quoted using the
|
|
||||||
* -quote: method.
|
|
||||||
* </p>
|
* </p>
|
||||||
* <example>
|
* <example>
|
||||||
* result = [db query: @"SELECT Name FROM ", table, nil];
|
* result = [db query: @"SELECT Name FROM ", table, nil];
|
||||||
|
@ -579,8 +645,8 @@ extern unsigned SQLClientTimeTick();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes the query statement and substitutes in values from
|
* Takes the query statement and substitutes in values from
|
||||||
* the dictionary where markup of the format {key} is found.<br />
|
* the dictionary (in the same manner as the -buildQuery:with: method)
|
||||||
* Passes the result to the -query:,... method to execute.
|
* then executes the query and returns the response.<br />
|
||||||
* <example>
|
* <example>
|
||||||
* result = [db query: @"SELECT Name FROM {Table} WHERE ID = {ID}"
|
* result = [db query: @"SELECT Name FROM {Table} WHERE ID = {ID}"
|
||||||
* with: values];
|
* with: values];
|
||||||
|
@ -702,11 +768,16 @@ extern unsigned SQLClientTimeTick();
|
||||||
- (void) simpleExecute: (NSArray*)info;
|
- (void) simpleExecute: (NSArray*)info;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls -backendQuery: in a safe manner.<br />
|
* Calls -simpleQuery:recordClass: with the default record class.
|
||||||
|
*/
|
||||||
|
- (NSMutableArray*) simpleQuery: (NSString*)stmt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls -backendQuery:recordClass: in a safe manner.<br />
|
||||||
* Handles locking.<br />
|
* Handles locking.<br />
|
||||||
* Maintains -lastOperation date.
|
* Maintains -lastOperation date.
|
||||||
*/
|
*/
|
||||||
- (NSMutableArray*) simpleQuery: (NSString*)stmt;
|
- (NSMutableArray*) simpleQuery: (NSString*)stmt recordClass: (Class)cls;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the database user for this instance (or nil).
|
* Return the database user for this instance (or nil).
|
||||||
|
@ -802,7 +873,8 @@ extern unsigned SQLClientTimeTick();
|
||||||
* <p>Perform arbitrary query <em>which returns values.</em>
|
* <p>Perform arbitrary query <em>which returns values.</em>
|
||||||
* </p>
|
* </p>
|
||||||
* <example>
|
* <example>
|
||||||
* result = [db backendQuery: @"SELECT Name FROM Table"];
|
* result = [db backendQuery: @"SELECT Name FROM Table"
|
||||||
|
* recordClass: [SQLRecord class]];
|
||||||
* </example>
|
* </example>
|
||||||
* <p>Upon error, an exception is raised.
|
* <p>Upon error, an exception is raised.
|
||||||
* </p>
|
* </p>
|
||||||
|
@ -821,6 +893,16 @@ extern unsigned SQLClientTimeTick();
|
||||||
* <p>Application code must <em>not</em> call this method directly, it is
|
* <p>Application code must <em>not</em> call this method directly, it is
|
||||||
* for internal use only.
|
* for internal use only.
|
||||||
* </p>
|
* </p>
|
||||||
|
* <p>The cls argument specifies a subclass of [SQLRecord] to be used to
|
||||||
|
* create the records produced by the query.<br />
|
||||||
|
* This is provided as a performance optimisation when you want to store
|
||||||
|
* data directly into a special class of your own.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
- (NSMutableArray*) backendQuery: (NSString*)stmt recordClass: (Class)cls;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls -backendQuery:recordClass: with the default record class.
|
||||||
*/
|
*/
|
||||||
- (NSMutableArray*) backendQuery: (NSString*)stmt;
|
- (NSMutableArray*) backendQuery: (NSString*)stmt;
|
||||||
|
|
||||||
|
@ -1015,6 +1097,11 @@ extern unsigned SQLClientTimeTick();
|
||||||
query: (NSString*)stmt
|
query: (NSString*)stmt
|
||||||
with: (NSDictionary*)values;
|
with: (NSDictionary*)values;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls -cache:simpleQuery:recordClass: with the default record class.
|
||||||
|
*/
|
||||||
|
- (NSMutableArray*) cache: (int)seconds simpleQuery: (NSString*)stmt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the result of the query is already cached and is still valid,
|
* If the result of the query is already cached and is still valid,
|
||||||
* return it. Otherwise, perform the query and cache the result
|
* return it. Otherwise, perform the query and cache the result
|
||||||
|
@ -1026,7 +1113,9 @@ extern unsigned SQLClientTimeTick();
|
||||||
* Handles locking.<br />
|
* Handles locking.<br />
|
||||||
* Maintains -lastOperation date.
|
* Maintains -lastOperation date.
|
||||||
*/
|
*/
|
||||||
- (NSMutableArray*) cache: (int)seconds simpleQuery: (NSString*)stmt;
|
- (NSMutableArray*) cache: (int)seconds
|
||||||
|
simpleQuery: (NSString*)stmt
|
||||||
|
recordClass: (Class)cls;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the cache to be used by the receiver for storing the results of
|
* Sets the cache to be used by the receiver for storing the results of
|
||||||
|
|
325
SQLClient.m
325
SQLClient.m
|
@ -285,6 +285,9 @@ typedef struct {
|
||||||
@defs(SQLTransaction);
|
@defs(SQLTransaction);
|
||||||
} *TDefs;
|
} *TDefs;
|
||||||
|
|
||||||
|
@class _SQLRecord;
|
||||||
|
static Class rClass = 0;
|
||||||
|
|
||||||
@implementation SQLRecord
|
@implementation SQLRecord
|
||||||
+ (id) allocWithZone: (NSZone*)aZone
|
+ (id) allocWithZone: (NSZone*)aZone
|
||||||
{
|
{
|
||||||
|
@ -298,16 +301,226 @@ typedef struct {
|
||||||
if (null == nil)
|
if (null == nil)
|
||||||
{
|
{
|
||||||
null = [NSNull new];
|
null = [NSNull new];
|
||||||
|
rClass = [_SQLRecord class];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (id) newWithValues: (id*)v keys: (NSString**)k count: (unsigned int)c
|
||||||
|
{
|
||||||
|
return [rClass newWithValues: v keys: k count: c];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray*) allKeys
|
||||||
|
{
|
||||||
|
unsigned count = [self count];
|
||||||
|
id buf[count];
|
||||||
|
|
||||||
|
while (count-- > 0)
|
||||||
|
{
|
||||||
|
buf[count] = [self keyAtIndex: count];
|
||||||
|
}
|
||||||
|
return [NSArray arrayWithObjects: buf count: count];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id) copyWithZone: (NSZone*)z
|
||||||
|
{
|
||||||
|
return RETAIN(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (unsigned int) count
|
||||||
|
{
|
||||||
|
[self subclassResponsibility: _cmd];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSMutableDictionary*) dictionary
|
||||||
|
{
|
||||||
|
unsigned count = [self count];
|
||||||
|
id keys[count];
|
||||||
|
id vals[count];
|
||||||
|
|
||||||
|
[self getKeys: keys];
|
||||||
|
[self getObjects: vals];
|
||||||
|
return [NSMutableDictionary dictionaryWithObjects: vals
|
||||||
|
forKeys: keys
|
||||||
|
count: count];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) getKeys: (id*)buf
|
||||||
|
{
|
||||||
|
unsigned i = [self count];
|
||||||
|
|
||||||
|
while (i-- > 0)
|
||||||
|
{
|
||||||
|
buf[i] = [self keyAtIndex: i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) getObjects: (id*)buf
|
||||||
|
{
|
||||||
|
unsigned i = [self count];
|
||||||
|
|
||||||
|
while (i-- > 0)
|
||||||
|
{
|
||||||
|
buf[i] = [self objectAtIndex: i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id) init
|
||||||
|
{
|
||||||
|
NSLog(@"Illegal attempt to -init an SQLRecord");
|
||||||
|
DESTROY(self);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*) keyAtIndex: (unsigned int)pos
|
||||||
|
{
|
||||||
|
return [self subclassResponsibility: _cmd];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id) objectAtIndex: (unsigned int)pos
|
||||||
|
{
|
||||||
|
return [self subclassResponsibility: _cmd];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id) objectForKey: (NSString*)key
|
||||||
|
{
|
||||||
|
unsigned count = [self count];
|
||||||
|
unsigned pos;
|
||||||
|
id keys[count];
|
||||||
|
|
||||||
|
[self getKeys: keys];
|
||||||
|
for (pos = 0; pos < count; pos++)
|
||||||
|
{
|
||||||
|
if ([key isEqualToString: keys[pos]] == YES)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pos == count)
|
||||||
|
{
|
||||||
|
for (pos = 0; pos < count; pos++)
|
||||||
|
{
|
||||||
|
if ([key caseInsensitiveCompare: keys[pos]] == NSOrderedSame)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos == count)
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return [self objectAtIndex: pos];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) replaceObjectAtIndex: (unsigned)index withObject: (id)anObject
|
||||||
|
{
|
||||||
|
[self subclassResponsibility: _cmd];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) setObject: (id)anObject forKey: (NSString*)aKey
|
||||||
|
{
|
||||||
|
unsigned count = [self count];
|
||||||
|
unsigned pos;
|
||||||
|
id keys[count];
|
||||||
|
|
||||||
|
if (anObject == nil)
|
||||||
|
{
|
||||||
|
anObject = null;
|
||||||
|
}
|
||||||
|
[self getKeys: keys];
|
||||||
|
for (pos = 0; pos < count; pos++)
|
||||||
|
{
|
||||||
|
if ([aKey isEqualToString: keys[pos]] == YES)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pos == count)
|
||||||
|
{
|
||||||
|
for (pos = 0; pos < count; pos++)
|
||||||
|
{
|
||||||
|
if ([aKey caseInsensitiveCompare: keys[pos]] == NSOrderedSame)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos == count)
|
||||||
|
{
|
||||||
|
[NSException raise: NSInvalidArgumentException
|
||||||
|
format: @"Bad key (%@) in -setObject:forKey:", aKey];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[self replaceObjectAtIndex: pos withObject: anObject];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (unsigned) sizeInBytes: (NSMutableSet*)exclude
|
||||||
|
{
|
||||||
|
if ([exclude member: self] != nil)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned size = [super sizeInBytes: exclude];
|
||||||
|
unsigned pos;
|
||||||
|
unsigned count = [self count];
|
||||||
|
id vals[count];
|
||||||
|
|
||||||
|
[self getObjects: vals];
|
||||||
|
for (pos = 0; pos < count; pos++)
|
||||||
|
{
|
||||||
|
size += [vals[pos] sizeInBytes: exclude];
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SQLRecord (KVC)
|
||||||
|
- (void) setValue: (id)aValue forKey: (NSString*)aKey
|
||||||
|
{
|
||||||
|
[self setObject: aValue forKey: aKey];
|
||||||
|
}
|
||||||
|
- (id) valueForKey: (NSString*)aKey
|
||||||
|
{
|
||||||
|
id v = [self objectForKey: aKey];
|
||||||
|
|
||||||
|
if (v == nil)
|
||||||
|
{
|
||||||
|
v = [super valueForKey: aKey];
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface _SQLRecord : SQLRecord
|
||||||
|
{
|
||||||
|
unsigned count;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation _SQLRecord
|
||||||
|
|
||||||
+ (id) newWithValues: (id*)v keys: (NSString**)k count: (unsigned int)c
|
+ (id) newWithValues: (id*)v keys: (NSString**)k count: (unsigned int)c
|
||||||
{
|
{
|
||||||
id *ptr;
|
id *ptr;
|
||||||
SQLRecord *r;
|
_SQLRecord *r;
|
||||||
unsigned pos;
|
unsigned pos;
|
||||||
|
|
||||||
r = (SQLRecord*)NSAllocateObject(self, c*2*sizeof(id), NSDefaultMallocZone());
|
r = (_SQLRecord*)NSAllocateObject(self,
|
||||||
|
c*2*sizeof(id), NSDefaultMallocZone());
|
||||||
r->count = c;
|
r->count = c;
|
||||||
ptr = ((void*)&(r->count)) + sizeof(r->count);
|
ptr = ((void*)&(r->count)) + sizeof(r->count);
|
||||||
for (pos = 0; pos < c; pos++)
|
for (pos = 0; pos < c; pos++)
|
||||||
|
@ -372,6 +585,19 @@ typedef struct {
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) getKeys: (id*)buf
|
||||||
|
{
|
||||||
|
id *ptr;
|
||||||
|
unsigned pos;
|
||||||
|
|
||||||
|
ptr = ((void*)&count) + sizeof(count);
|
||||||
|
ptr += count; // Step past objects to keys.
|
||||||
|
for (pos = 0; pos < count; pos++)
|
||||||
|
{
|
||||||
|
buf[pos] = ptr[pos];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void) getObjects: (id*)buf
|
- (void) getObjects: (id*)buf
|
||||||
{
|
{
|
||||||
id *ptr;
|
id *ptr;
|
||||||
|
@ -391,6 +617,20 @@ typedef struct {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (id) keyAtIndex: (unsigned int)pos
|
||||||
|
{
|
||||||
|
id *ptr;
|
||||||
|
|
||||||
|
if (pos >= count)
|
||||||
|
{
|
||||||
|
[NSException raise: NSRangeException
|
||||||
|
format: @"Array index too large"];
|
||||||
|
}
|
||||||
|
ptr = ((void*)&count) + sizeof(count);
|
||||||
|
ptr += count;
|
||||||
|
return ptr[pos];
|
||||||
|
}
|
||||||
|
|
||||||
- (id) objectAtIndex: (unsigned int)pos
|
- (id) objectAtIndex: (unsigned int)pos
|
||||||
{
|
{
|
||||||
id *ptr;
|
id *ptr;
|
||||||
|
@ -427,6 +667,24 @@ typedef struct {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) replaceObjectAtIndex: (unsigned)index withObject: (id)anObject
|
||||||
|
{
|
||||||
|
id *ptr;
|
||||||
|
|
||||||
|
if (index >= count)
|
||||||
|
{
|
||||||
|
[NSException raise: NSRangeException
|
||||||
|
format: @"Array index too large"];
|
||||||
|
}
|
||||||
|
if (anObject == nil)
|
||||||
|
{
|
||||||
|
anObject = null;
|
||||||
|
}
|
||||||
|
ptr = ((void*)&count) + sizeof(count);
|
||||||
|
ptr += index;
|
||||||
|
ASSIGN(*ptr, anObject);
|
||||||
|
}
|
||||||
|
|
||||||
- (void) setObject: (id)anObject forKey: (NSString*)aKey
|
- (void) setObject: (id)anObject forKey: (NSString*)aKey
|
||||||
{
|
{
|
||||||
id *ptr;
|
id *ptr;
|
||||||
|
@ -643,10 +901,7 @@ static unsigned int maxConnections = 8;
|
||||||
+ (void) initialize
|
+ (void) initialize
|
||||||
{
|
{
|
||||||
GSTickerTimeNow();
|
GSTickerTimeNow();
|
||||||
if (null == nil)
|
[SQLRecord class]; // Force initialisation
|
||||||
{
|
|
||||||
null = [NSNull new];
|
|
||||||
}
|
|
||||||
if (clientsMap == 0)
|
if (clientsMap == 0)
|
||||||
{
|
{
|
||||||
clientsMap = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
clientsMap = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
||||||
|
@ -770,6 +1025,30 @@ static unsigned int maxConnections = 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString*) buildQuery: (NSString*)stmt, ...
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
NSString *sql = nil;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First check validity and concatenate parts of the query.
|
||||||
|
*/
|
||||||
|
va_start (ap, stmt);
|
||||||
|
sql = [[self _prepare: stmt args: ap] objectAtIndex: 0];
|
||||||
|
va_end (ap);
|
||||||
|
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*) buildQuery: (NSString*)stmt with: (NSDictionary*)values
|
||||||
|
{
|
||||||
|
NSString *sql = nil;
|
||||||
|
|
||||||
|
sql = [[self _substitute: stmt with: values] objectAtIndex: 0];
|
||||||
|
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
- (NSString*) clientName
|
- (NSString*) clientName
|
||||||
{
|
{
|
||||||
return _client;
|
return _client;
|
||||||
|
@ -1447,9 +1726,19 @@ static unsigned int maxConnections = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSMutableArray*) simpleQuery: (NSString*)stmt
|
- (NSMutableArray*) simpleQuery: (NSString*)stmt
|
||||||
|
{
|
||||||
|
return [self simpleQuery: stmt recordClass: rClass];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSMutableArray*) simpleQuery: (NSString*)stmt recordClass: (Class)cls
|
||||||
{
|
{
|
||||||
NSMutableArray *result = nil;
|
NSMutableArray *result = nil;
|
||||||
|
|
||||||
|
if (cls == 0)
|
||||||
|
{
|
||||||
|
[NSException raise: NSInvalidArgumentException
|
||||||
|
format: @"nil class passed to simpleQuery:recordClass:"];
|
||||||
|
}
|
||||||
[lock lock];
|
[lock lock];
|
||||||
NS_DURING
|
NS_DURING
|
||||||
{
|
{
|
||||||
|
@ -1459,7 +1748,7 @@ static unsigned int maxConnections = 8;
|
||||||
{
|
{
|
||||||
start = GSTickerTimeNow();
|
start = GSTickerTimeNow();
|
||||||
}
|
}
|
||||||
result = [self backendQuery: stmt];
|
result = [self backendQuery: stmt recordClass: cls];
|
||||||
_lastOperation = GSTickerTimeNow();
|
_lastOperation = GSTickerTimeNow();
|
||||||
if (_duration >= 0)
|
if (_duration >= 0)
|
||||||
{
|
{
|
||||||
|
@ -1514,6 +1803,11 @@ static unsigned int maxConnections = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSMutableArray*) backendQuery: (NSString*)stmt
|
- (NSMutableArray*) backendQuery: (NSString*)stmt
|
||||||
|
{
|
||||||
|
return [self backendQuery: stmt recordClass: rClass];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSMutableArray*) backendQuery: (NSString*)stmt recordClass: (Class)cls
|
||||||
{
|
{
|
||||||
[NSException raise: NSInternalInconsistencyException
|
[NSException raise: NSInternalInconsistencyException
|
||||||
format: @"Called -%@ without backend bundle loaded",
|
format: @"Called -%@ without backend bundle loaded",
|
||||||
|
@ -2126,10 +2420,23 @@ static unsigned int maxConnections = 8;
|
||||||
return [self cache: seconds simpleQuery: stmt];
|
return [self cache: seconds simpleQuery: stmt];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSMutableArray*) cache: (int)seconds simpleQuery: (NSString*)stmt
|
- (NSMutableArray*) cache: (int)seconds
|
||||||
|
simpleQuery: (NSString*)stmt
|
||||||
|
{
|
||||||
|
return [self cache: seconds simpleQuery: stmt recordClass: rClass];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSMutableArray*) cache: (int)seconds
|
||||||
|
simpleQuery: (NSString*)stmt
|
||||||
|
recordClass: (Class)cls
|
||||||
{
|
{
|
||||||
NSMutableArray *result = nil;
|
NSMutableArray *result = nil;
|
||||||
|
|
||||||
|
if (cls == 0)
|
||||||
|
{
|
||||||
|
[NSException raise: NSInvalidArgumentException
|
||||||
|
format: @"nil class passed to cache:simpleQuery:recordClass:"];
|
||||||
|
}
|
||||||
[lock lock];
|
[lock lock];
|
||||||
NS_DURING
|
NS_DURING
|
||||||
{
|
{
|
||||||
|
@ -2148,7 +2455,7 @@ static unsigned int maxConnections = 8;
|
||||||
|
|
||||||
if (result == nil)
|
if (result == nil)
|
||||||
{
|
{
|
||||||
result = toCache = [self backendQuery: stmt];
|
result = toCache = [self backendQuery: stmt recordClass: cls];
|
||||||
_lastOperation = GSTickerTimeNow();
|
_lastOperation = GSTickerTimeNow();
|
||||||
if (_duration >= 0)
|
if (_duration >= 0)
|
||||||
{
|
{
|
||||||
|
|
8
SQLite.m
8
SQLite.m
|
@ -194,7 +194,7 @@
|
||||||
DESTROY(arp);
|
DESTROY(arp);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSMutableArray*) backendQuery: (NSString*)stmt
|
- (NSMutableArray*) backendQuery: (NSString*)stmt recordClass: (Class)rClass
|
||||||
{
|
{
|
||||||
CREATE_AUTORELEASE_POOL(arp);
|
CREATE_AUTORELEASE_POOL(arp);
|
||||||
NSMutableArray *records = [[NSMutableArray alloc] init];
|
NSMutableArray *records = [[NSMutableArray alloc] init];
|
||||||
|
@ -281,9 +281,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
record = [SQLRecord newWithValues: values
|
record = [rClass newWithValues: values
|
||||||
keys: keys
|
keys: keys
|
||||||
count: columns];
|
count: columns];
|
||||||
[records addObject: record];
|
[records addObject: record];
|
||||||
RELEASE(record);
|
RELEASE(record);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue