Add a proper build system

FossilOrigin-Name: 8679c61b2c82da9daa316ed5eb90ba1b69e608127afa318cbdbff56b53d65a74
This commit is contained in:
Jonathan Schleifer 2014-07-18 23:35:17 +00:00
parent 71342537cf
commit 35212e3305
28 changed files with 5118 additions and 76 deletions

22
src/Makefile Normal file
View file

@ -0,0 +1,22 @@
include ../extra.mk
SUBDIRS = exceptions
SHARED_LIB = ${OBJPGSQL_SHARED_LIB}
STATIC_LIB = ${OBJPGSQL_STATIC_LIB}
LIB_MAJOR = 0
LIB_MINOR = 1
SRCS = PGConnection.m \
PGResult.m \
PGResultRow.m
INCLUDES := ${SRCS:.m=.h} \
ObjPgSQL.h
OBJS_EXTRA = ${EXCEPTIONS_EXCEPTIONS_A}
LIB_OBJS_EXTRA = ${EXCEPTIONS_EXCEPTIONS_LIB_A}
include ../buildsys.mk
CPPFLAGS += -I. -Iexceptions
LD = ${OBJC}

7
src/ObjPgSQL.h Normal file
View file

@ -0,0 +1,7 @@
#import "PGResult.h"
#import "PGResultRow.h"
#import "PGConnection.h"
#import "PGException.h"
#import "PGCommandFailedException.h"
#import "PGConnectionFailedException.h"

30
src/PGConnection.h Normal file
View file

@ -0,0 +1,30 @@
#include <libpq-fe.h>
#import <ObjFW/ObjFW.h>
#import "PGResult.h"
@interface PGConnection: OFObject
{
PGconn *_connnection;
OFDictionary *_parameters;
}
#ifdef OF_HAVE_PROPERTIES
@property (copy) OFDictionary *parameters;
#endif
- (void)setParameters: (OFDictionary*)parameters;
- (OFDictionary*)parameters;
- (void)connect;
- (void)reset;
- (void)close;
- (PGResult*)executeCommand: (OFConstantString*)command;
- (PGResult*)executeCommand: (OFConstantString*)command
parameters: (id)firstParameter, ... OF_SENTINEL;
- (PGconn*)PG_connection;
- (void)insertRow: (OFDictionary*)row
intoTable: (OFString*)table;
- (void)insertRows: (OFArray*)rows
intoTable: (OFString*)table;
@end

239
src/PGConnection.m Normal file
View file

@ -0,0 +1,239 @@
#import "PGConnection.h"
#import "PGConnectionFailedException.h"
#import "PGCommandFailedException.h"
@implementation PGConnection
- (void)dealloc
{
[_parameters release];
[self close];
[super dealloc];
}
- (void)setParameters: (OFDictionary*)parameters
{
OF_SETTER(_parameters, parameters, YES, YES)
}
- (OFDictionary*)parameters
{
OF_GETTER(_parameters, YES)
}
- (void)connect
{
OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
OFEnumerator *keyEnumerator = [_parameters keyEnumerator];
OFEnumerator *objectEnumerator = [_parameters objectEnumerator];
OFMutableString *connectionInfo = nil;
OFString *key, *object;
while ((key = [keyEnumerator nextObject]) != nil &&
(object = [objectEnumerator nextObject]) != nil) {
if (connectionInfo != nil)
[connectionInfo appendFormat: @" %@=%@", key, object];
else
connectionInfo = [OFMutableString stringWithFormat:
@"%@=%@", key, object];
}
if ((_connnection = PQconnectdb([connectionInfo UTF8String])) == NULL)
@throw [OFOutOfMemoryException exception];
if (PQstatus(_connnection) == CONNECTION_BAD)
@throw [PGConnectionFailedException
exceptionWithConnection: self];
[pool release];
}
- (void)reset
{
PQreset(_connnection);
}
- (void)close
{
if (_connnection != NULL)
PQfinish(_connnection);
_connnection = NULL;
}
- (PGResult*)executeCommand: (OFConstantString*)command
{
PGresult *result = PQexec(_connnection, [command UTF8String]);
if (PQresultStatus(result) == PGRES_FATAL_ERROR) {
PQclear(result);
@throw [PGCommandFailedException
exceptionWithConnection: self
command: command];
}
switch (PQresultStatus(result)) {
case PGRES_TUPLES_OK:
return [PGResult PG_resultWithResult: result];
case PGRES_COMMAND_OK:
PQclear(result);
return nil;
default:
PQclear(result);
@throw [PGCommandFailedException
exceptionWithConnection: self
command: command];
}
}
- (PGResult*)executeCommand: (OFConstantString*)command
parameters: (id)parameter, ...
{
OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
PGresult *result;
const char **values;
va_list args, args2;
int argsCount;
va_start(args, parameter);
va_copy(args2, args);
for (argsCount = 1; va_arg(args2, id) != nil; argsCount++);
values = [self allocMemoryWithSize: sizeof(*values)
count: argsCount];
@try {
size_t i = 0;
do {
if ([parameter isKindOfClass: [OFString class]])
values[i++] = [parameter UTF8String];
else if ([parameter isKindOfClass: [OFNumber class]]) {
OFNumber *number = parameter;
switch ([number type]) {
case OF_NUMBER_TYPE_BOOL:
if ([number boolValue])
values[i++] = "t";
else
values[i++] = "f";
break;
default:
values[i++] = [[number description]
UTF8String];
break;
}
} else if ([parameter isKindOfClass: [OFNull class]])
values[i++] = NULL;
else
values[i++] = [[parameter description]
UTF8String];
} while ((parameter = va_arg(args, id)) != nil);
result = PQexecParams(_connnection, [command UTF8String],
argsCount, NULL, values, NULL, NULL, 0);
} @finally {
[self freeMemory: values];
}
[pool release];
switch (PQresultStatus(result)) {
case PGRES_TUPLES_OK:
return [PGResult PG_resultWithResult: result];
case PGRES_COMMAND_OK:
PQclear(result);
return nil;
default:
PQclear(result);
@throw [PGCommandFailedException
exceptionWithConnection: self
command: command];
}
}
- (void)insertRow: (OFDictionary*)row
intoTable: (OFString*)table
{
OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
OFMutableString *command;
OFEnumerator *enumerator;
const char **values;
PGresult *result;
OFString *key, *value;
size_t i, count;
command = [OFMutableString stringWithString: @"INSERT INTO "];
[command appendString: table];
[command appendString: @" ("];
count = [row count];
i = 0;
enumerator = [row keyEnumerator];
while ((key = [enumerator nextObject]) != nil) {
if (i > 0)
[command appendString: @", "];
[command appendString: key];
i++;
}
[command appendString: @") VALUES ("];
values = [self allocMemoryWithSize: sizeof(*values)
count: count];
@try {
i = 0;
enumerator = [row objectEnumerator];
while ((value = [enumerator nextObject]) != nil) {
if (i > 0)
[command appendString: @", "];
values[i] = [value UTF8String];
[command appendFormat: @"$%zd", ++i];
}
[command appendString: @")"];
result = PQexecParams(_connnection, [command UTF8String],
(int)count, NULL, values, NULL, NULL, 0);
} @finally {
[self freeMemory: values];
}
[pool release];
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
PQclear(result);
@throw [PGCommandFailedException
exceptionWithConnection: self
command: command];
}
PQclear(result);
}
- (void)insertRows: (OFArray*)rows
intoTable: (OFString*)table
{
OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
OFEnumerator *enumerator = [rows objectEnumerator];
OFDictionary *row;
while ((row = [enumerator nextObject]) != nil)
[self insertRow: row
intoTable: table];
[pool release];
}
- (PGconn*)PG_connection
{
return _connnection;
}
@end

13
src/PGResult.h Normal file
View file

@ -0,0 +1,13 @@
#include <libpq-fe.h>
#import <ObjFW/ObjFW.h>
@interface PGResult: OFArray
{
PGresult *_result;
}
+ PG_resultWithResult: (PGresult*)result;
- PG_initWithResult: (PGresult*)result;
- (PGresult*)PG_result;
@end

45
src/PGResult.m Normal file
View file

@ -0,0 +1,45 @@
#import "PGResult.h"
#import "PGResultRow.h"
@implementation PGResult
+ PG_resultWithResult: (PGresult*)result
{
return [[[self alloc] PG_initWithResult: result] autorelease];
}
- PG_initWithResult: (PGresult*)result
{
self = [super init];
_result = result;
return self;
}
- (void)dealloc
{
if (_result != NULL)
PQclear(_result);
[super dealloc];
}
- (size_t)count
{
return PQntuples(_result);
}
- (id)objectAtIndex: (size_t)index
{
if (index > PQntuples(_result))
@throw [OFOutOfRangeException exception];
return [PGResultRow rowWithResult: self
row: (int)index];
}
- (PGresult*)PG_result
{
return _result;
}
@end

18
src/PGResultRow.h Normal file
View file

@ -0,0 +1,18 @@
#include <libpq-fe.h>
#import <ObjFW/ObjFW.h>
#import "PGResult.h"
@interface PGResultRow: OFDictionary
{
PGResult *_result;
PGresult *_res;
int _row;
}
+ rowWithResult: (PGResult*)result
row: (int)row;
- initWithResult: (PGResult*)result
row: (int)row;
@end

206
src/PGResultRow.m Normal file
View file

@ -0,0 +1,206 @@
#import "PGResultRow.h"
static id
convertType(PGresult *res, int column, OFString *string)
{
switch (PQftype(res, column)) {
case 16: /* BOOLOID */
if ([string isEqual: @"t"])
return [OFNumber numberWithBool: YES];
else
return [OFNumber numberWithBool: NO];
case 21: /* INT2OID */
return [OFNumber numberWithInt16:
(int16_t)[string decimalValue]];
case 23: /* INT4OID */
return [OFNumber numberWithInt32:
(int32_t)[string decimalValue]];
case 20: /* INT8OID */
return [OFNumber numberWithInt64:
(int64_t)[string decimalValue]];
case 700: /* FLOAT4OID */
return [OFNumber numberWithFloat: [string floatValue]];
case 701: /* FLOAT8OID */
return [OFNumber numberWithDouble: [string doubleValue]];
}
return string;
}
@interface PGResultRowEnumerator: OFEnumerator
{
PGResult *_result;
PGresult *_res;
int _row, _pos, _count;
}
- initWithResult: (PGResult*)result
row: (int)row;
@end
@interface PGResultRowKeyEnumerator: PGResultRowEnumerator
@end
@interface PGResultRowObjectEnumerator: PGResultRowEnumerator
@end
@implementation PGResultRow
+ rowWithResult: (PGResult*)result
row: (int)row
{
return [[[self alloc] initWithResult: result
row: row] autorelease];
}
- initWithResult: (PGResult*)result
row: (int)row
{
self = [super init];
_result = [result retain];
_res = [result PG_result];
_row = row;
return self;
}
- (void)dealloc
{
[_result release];
[super dealloc];
}
- (size_t)count
{
int i, count, fields = PQnfields(_res);
for (i = count = 0; i < fields; i++)
if (!PQgetisnull(_res, _row, i))
count++;
return count;
}
- (id)objectForKey: (id)key
{
int column;
if ([key isKindOfClass: [OFNumber class]])
column = [key intValue];
else
column = PQfnumber(_res, [key UTF8String]);
if (PQgetisnull(_res, _row, column))
return nil;
return convertType(_res, column,
[OFString stringWithUTF8String: PQgetvalue(_res, _row, column)]);
}
- (OFEnumerator*)keyEnumerator
{
return [[[PGResultRowKeyEnumerator alloc]
initWithResult: _result
row: _row] autorelease];
}
- (OFEnumerator*)objectEnumerator
{
return [[[PGResultRowObjectEnumerator alloc]
initWithResult: _result
row: _row] autorelease];
}
- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t*)state
objects: (id*)objects
count: (int)count
{
int i, j;
if (state->extra[0] == 0) {
state->extra[0] = 1;
state->extra[1] = PQnfields(_res);
}
if (count > SIZE_MAX - state->state)
@throw [OFOutOfRangeException exception];
if (state->state + count > state->extra[1])
count = state->extra[1] - state->state;
for (i = j = 0; i < count; i++) {
if (PQgetisnull(_res, _row, state->state + i))
continue;
objects[j++] = [OFString stringWithUTF8String:
PQfname(_res, state->state + i)];
}
state->state += count;
state->itemsPtr = objects;
state->mutationsPtr = (unsigned long*)self;
return j;
}
@end
@implementation PGResultRowEnumerator
- initWithResult: (PGResult*)result
row: (int)row
{
self = [super init];
_result = [result retain];
_res = [result PG_result];
_row = row;
_count = PQnfields(_res);
return self;
}
- (void)dealloc
{
[_result release];
[super dealloc];
}
- (void)reset
{
_pos = 0;
}
@end
@implementation PGResultRowKeyEnumerator
- (id)nextObject
{
if (_pos >= _count)
return nil;
while (_pos < _count && PQgetisnull(_res, _row, _pos))
_pos++;
if (_pos >= _count)
return nil;
return [OFString stringWithUTF8String: PQfname(_res, _pos++)];
}
@end
@implementation PGResultRowObjectEnumerator
- (id)nextObject
{
if (_pos >= _count)
return nil;
while (_pos < _count && PQgetisnull(_res, _row, _pos))
_pos++;
if (_pos >= _count)
return nil;
return convertType(_res, _pos,
[OFString stringWithUTF8String: PQgetvalue(_res, _row, _pos++)]);
}
@end

14
src/exceptions/Makefile Normal file
View file

@ -0,0 +1,14 @@
include ../../extra.mk
STATIC_PIC_LIB_NOINST = ${EXCEPTIONS_LIB_A}
STATIC_LIB_NOINST = ${EXCEPTIONS_A}
SRCS = PGCommandFailedException.m \
PGConnectionFailedException.m \
PGException.m
INCLUDES = ${SRCS:.m=.h}
include ../../buildsys.mk
CPPFLAGS += -I. -I..

View file

@ -0,0 +1,17 @@
#import "PGException.h"
@interface PGCommandFailedException: PGException
{
OFString *_command;
}
#ifdef OF_HAVE_PROPERTIES
@property (readonly, copy, nonatomic) OFString *command;
#endif
+ (instancetype)exceptionWithConnection: (PGConnection*)connection
command: (OFString*)command;
- initWithConnection: (PGConnection*)connection
command: (OFString*)command;
- (OFString*)command;
@end

View file

@ -0,0 +1,43 @@
#import "PGCommandFailedException.h"
@implementation PGCommandFailedException
+ (instancetype)exceptionWithConnection: (PGConnection*)connection
command: (OFString*)command
{
return [[[self alloc] initWithConnection: connection
command: command] autorelease];
}
- initWithConnection: (PGConnection*)connection
command: (OFString*)command
{
self = [super initWithConnection: connection];
@try {
_command = [command copy];
} @catch (id e) {
[self release];
@throw e;
}
return self;
}
- (void)dealloc
{
[_command release];
[super dealloc];
}
- (OFString*)description
{
return [OFString stringWithFormat: @"A PostgreSQL command failed: %@\n"
@"Command: %@", _error, _command];
}
- (OFString*)command
{
OF_GETTER(_command, NO)
}
@end

View file

@ -0,0 +1,4 @@
#import "PGException.h"
@interface PGConnectionFailedException: PGException
@end

View file

@ -0,0 +1,10 @@
#import "PGConnectionFailedException.h"
@implementation PGConnectionFailedException
- (OFString*)description
{
return [OFString stringWithFormat:
@"Establishing a PostgreSQL connection failed:\n%@\n"
"Parameters: %@", _error, [_connection parameters]];
}
@end

View file

@ -0,0 +1,18 @@
#import <ObjFW/ObjFW.h>
#import "PGConnection.h"
@interface PGException: OFException
{
PGConnection *_connection;
OFString *_error;
}
#ifdef OF_HAVE_PROPERTIES
@property (readonly, retain, nonatomic) PGConnection *connection;
#endif
+ (instancetype)exceptionWithConnection: (PGConnection*)connection;
- initWithConnection: (PGConnection*)connection;
- (PGConnection*)connection;
@end

View file

@ -0,0 +1,44 @@
#import "PGException.h"
@implementation PGException
+ (instancetype)exceptionWithConnection: (PGConnection*)connection
{
return [[[self alloc] initWithConnection: connection] autorelease];
}
- initWithConnection: (PGConnection*)connection
{
self = [super init];
@try {
_connection = [connection retain];
_error = [[OFString alloc]
initWithCString: PQerrorMessage([_connection PG_connection])
encoding: [OFString nativeOSEncoding]];
} @catch (id e) {
[self release];
@throw e;
}
return self;
}
- (void)dealloc
{
[_connection release];
[_error release];
[super dealloc];
}
- (OFString*)description
{
return [OFString stringWithFormat: @"A PostgreSQL operation failed: %@",
_error];
}
- (PGConnection*)connection
{
OF_GETTER(_connection, NO)
}
@end