libs-sqlclient/SQLite.m
Richard Frith-MacDonald 56fc0088ad add helper classes
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/sqlclient/trunk@36261 72102866-910b-0410-8b05-ffd578937521
2013-03-04 14:47:29 +00:00

370 lines
8.4 KiB
Objective-C

/* -*-objc-*- */
/** Implementation of SQLClientSQLite for GNUStep
Copyright (C) 2005 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: Nov 2005
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/NSAutoreleasePool.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSData.h>
#import <Foundation/NSDate.h>
#import <Foundation/NSException.h>
#import <Foundation/NSLock.h>
#import <Foundation/NSMapTable.h>
#import <Foundation/NSNotification.h>
#import <Foundation/NSNull.h>
#import <Foundation/NSPathUtilities.h>
#import <Foundation/NSProcessInfo.h>
#import <Foundation/NSString.h>
#import <Foundation/NSUserDefaults.h>
#import <Foundation/NSValue.h>
#include "config.h"
#define SQLCLIENT_PRIVATE @public
#include "SQLClient.h"
#include <string.h>
#include <sqlite3.h>
@interface SQLClientSQLite : SQLClient
@end
@implementation SQLClientSQLite
/* use [self database] as path to database file */
- (BOOL) backendConnect
{
if (connected == NO)
{
if ([self database] != nil)
{
NSString *dbase = [self database];
sqlite3 *sql;
int result;
[[self class] purgeConnections: nil];
if ([self debugging] > 0)
{
[self debug: @"Connect to '%@' as %@",
[self database], [self name]];
}
result = sqlite3_open([dbase fileSystemRepresentation], &sql);
if (result != 0)
{
[self debug: @"Error connecting to '%@' (%@) - %s",
[self name], [self database], sqlite3_errmsg(sql)];
sqlite3_close(sql);
extra = 0;
}
else
{
connected = YES;
extra = sql;
if ([self debugging] > 0)
{
[self debug: @"Connected to '%@'", [self name]];
}
}
}
else
{
[self debug: @"Connect to '%@' with no database configured",
[self name]];
}
}
return connected;
}
- (void) backendDisconnect
{
if (connected == YES)
{
NS_DURING
{
if ([self isInTransaction] == YES)
{
[self rollback];
}
if ([self debugging] > 0)
{
[self debug: @"Disconnecting client %@", [self clientName]];
}
sqlite3_close((sqlite3 *)extra);
extra = 0;
if ([self debugging] > 0)
{
[self debug: @"Disconnected client %@", [self clientName]];
}
}
NS_HANDLER
{
extra = 0;
[self debug: @"Error disconnecting from database (%@): %@",
[self clientName], localException];
}
NS_ENDHANDLER
connected = NO;
}
}
- (NSInteger) backendExecute: (NSArray*)info
{
NSString *stmt;
NSAutoreleasePool *arp = [NSAutoreleasePool new];
stmt = [info objectAtIndex: 0];
if ([stmt length] == 0)
{
[arp release];
[NSException raise: NSInternalInconsistencyException
format: @"Statement produced null string"];
}
NS_DURING
{
const char *statement;
unsigned length;
int result;
char *err;
/*
* Ensure we have a working connection.
*/
if ([self connect] == NO)
{
[NSException raise: SQLException
format: @"Unable to connect to '%@' to execute statement %@",
[self name], stmt];
}
statement = (char*)[stmt UTF8String];
length = strlen(statement);
statement = [self insertBLOBs: info
intoStatement: statement
length: length
withMarker: "'?'''?'"
length: 7
giving: &length];
result = sqlite3_exec((sqlite3 *)extra, statement, 0, 0, &err);
if (result != SQLITE_OK)
{
[NSException raise: SQLException format: @"%s", err];
}
}
NS_HANDLER
{
NSString *n = [localException name];
if ([n isEqual: SQLConnectionException] == YES)
{
[self disconnect];
}
if ([self debugging] > 0)
{
[self debug: @"Error executing statement:\n%@\n%@",
stmt, localException];
}
[localException retain];
[arp release];
[localException autorelease];
[localException raise];
}
NS_ENDHANDLER
[arp release];
return -1;
}
- (NSMutableArray*) backendQuery: (NSString*)stmt
recordType: (id)rtype
listType: (id)ltype
{
NSAutoreleasePool *arp = [NSAutoreleasePool new];
NSMutableArray *records = [[ltype alloc] initWithCapacity: 2];
if ([stmt length] == 0)
{
[arp release];
[NSException raise: NSInternalInconsistencyException
format: @"Statement produced null string"];
}
NS_DURING
{
char *statement;
int result;
sqlite3_stmt *prepared;
const char *stmtEnd;
/*
* Ensure we have a working connection.
*/
if ([self connect] == NO)
{
[NSException raise: SQLException
format: @"Unable to connect to '%@' to run query %@",
[self name], stmt];
}
statement = (char*)[stmt UTF8String];
result = sqlite3_prepare((sqlite3 *)extra,
statement, strlen(statement), &prepared, &stmtEnd);
if (result != SQLITE_OK)
{
[NSException raise: SQLException
format: @"Unable to prepare '%@'", stmt];
}
if ((result = sqlite3_step(prepared)) == SQLITE_ROW)
{
int columns = sqlite3_column_count(prepared);
NSString *keys[columns];
int i;
for (i = 0; i < columns; i++)
{
keys[i] = [NSString stringWithUTF8String:
sqlite3_column_name(prepared, i)];
}
do
{
id values[columns];
SQLRecord *record;
for (i = 0; i < columns; i++)
{
int type = sqlite3_column_type(prepared, i);
switch (type)
{
case SQLITE_INTEGER:
values[i] = [NSNumber numberWithInt:
sqlite3_column_int(prepared, i)];
break;
case SQLITE_FLOAT:
values[i] = [NSNumber numberWithDouble:
sqlite3_column_double(prepared, i)];
break;
case SQLITE_TEXT:
values[i] = [NSString stringWithUTF8String:
(char *)sqlite3_column_text(prepared, i)];
break;
case SQLITE_BLOB:
values[i] = [NSData dataWithBytes:
sqlite3_column_blob(prepared, i)
length: sqlite3_column_bytes(prepared, i)];
break;
case SQLITE_NULL:
values[i] = nil;
break;
}
}
record = [rtype newWithValues: values
keys: keys
count: columns];
[records addObject: record];
[record release];
}
while ((result = sqlite3_step(prepared)) == SQLITE_ROW);
}
if (result != SQLITE_DONE)
{
[NSException raise: SQLException
format: @"%s", sqlite3_errmsg((sqlite3 *)extra)];
}
sqlite3_finalize(prepared);
}
NS_HANDLER
{
NSString *n = [localException name];
if ([n isEqual: SQLConnectionException] == YES)
{
[self disconnect];
}
if ([self debugging] > 0)
{
[self debug: @"Error executing statement:\n%@\n%@",
stmt, localException];
}
[localException retain];
[arp release];
[localException autorelease];
[localException raise];
}
NS_ENDHANDLER
[arp release];
return [records autorelease];
}
static char hex[16] = "0123456789ABCDEF";
- (unsigned) copyEscapedBLOB: (NSData*)blob into: (void*)buf
{
const unsigned char *bytes = [blob bytes];
unsigned char *ptr = buf;
unsigned length = [blob length];
unsigned i;
*ptr++ = 'X';
*ptr++ = '\'';
for (i = 0; i < length; i++)
{
unsigned char c = bytes[i];
*ptr++ = hex[c / 16];
*ptr++ = hex[c % 16];;
}
*ptr++ = '\'';
return ((void*)ptr - buf);
}
- (unsigned) lengthOfEscapedBLOB: (NSData*)blob
{
/*
* A blob is X'xx' where xx is hexadecimal encoded binary data ...
* two hex digits per byte.
*/
return 3 + [blob length] * 2;
}
- (NSString*) quote: (id)obj
{
if ([obj isKindOfClass: [NSDate class]] == YES)
{
obj = [NSNumber numberWithDouble: [obj timeIntervalSinceReferenceDate]];
}
return [super quote: obj];
}
@end