См. также:
Создание фреймворка для postgreSQL под OS X Xcode.

 

Загрузить файлы проекта

Обертка Objective-C iOS для postgreSQL API

Итак, мы создали в предъидущем разделе необходимую нам библиотеку libpq, содержащую клиентскую часть API для подключения к серверу postgreSQL.

Итак для начала создадим класс соединения с базой данных унаследованный от NSObject и назовем его mbDB_PG.

Подключение к БД, класс mbDB_PG

Листинг заголовочного файла mbDB_PG.h

#import <UIKit/UIKit.h>
#import <libpq-fe.h>

@interface mbDB_PG : NSObject
{
  NSString* serverName;//путь к серверу
  NSString* databaseName;//имя базы данных
  NSInteger port;//порт
  NSString* userName;
  NSString* password;
  PGconn* headerPG;//идентификатор соединения с БД, используется во всех функциях postgreSQL API
  NSString* lastError;
}
@property (readonly) PGconn* headerPG;
@property (copy) NSString* serverName;
@property (copy) NSString* databaseName;
@property NSInteger port;
@property (copy) NSString* userName;
@property (copy) NSString* password;
@property (readonly) NSString* lastError;

- (id)initWithServerName:(NSString*) sName dbName:(NSString*)dbName port:(NSInteger)prt;
- (id)initWithDB:(mbDB_PG*)db;
- (void)connect;
- (void)connectToDBwithUser:(NSString*)user password:(NSString*)psw;
- (BOOL)connected;
- (void)errorPG;
- (BOOL)isBusy;
- (void)disconnect;
- (NSString*) stringFromDate:(NSDate*)dt;
@end

Листинг файла реализации mbDB_PG.m

#import "mbDB_PG.h"
#import "mbErrorPG.h"

@interface mbDB_PG()

@end

@implementation mbDB_PG
@synthesize headerPG;
@synthesize serverName, databaseName, port, userName, password, lastError;

- (id)init
{
  self = [super init];
  if (self) {
    serverName=@"localhost";
    databaseName=@"newDatabaseName";
    port=5432;
  }
  return self;
}

- (id)initWithServerName:(NSString*) sName dbName:(NSString*)dbName port:(NSInteger)prt
{
  self = [super init];
  if (self) {
    if(sName)
      serverName = sName;
    else 
      serverName = @"localhost";
    if(dbName)
      databaseName = dbName;
    else
      databaseName = @"postgres";
    if(prt > 0)
      port = prt;
    else 
      port = 5432;//порт по умолчанию
  }
  return self;
}

- (id)initWithDB:(mbDB_PG*)db
{
  self = [super init];
  if(self){
    serverName = db.serverName;
    databaseName = db.databaseName;
    port = db.port;
    userName = db.userName;
    password = db.password;
  }
  return self;
}

- (void)connect
{
  [self disconnect];
  NSString* portS = [NSNumber numberWithInteger:port].stringValue;//Строковое представление порта
  headerPG = PQsetdbLogin(serverName.UTF8String,portS.UTF8String,NULL,NULL,databaseName.UTF8String,
                      userName.UTF8String, password.UTF8String);
  BOOL result = [self connected];//Проверить получилось ли подключиться можно только с помощью функции 
  // PQstatus API postreSQL
  if(!result){
    //Error connection
    [self errorPG];
  }
}

- (void)connectToDBwithUser:(NSString*)user password:(NSString*)psw
{
  userName = user;
  password = psw;
  [self connect];
}

- (BOOL) connected // Возвращает YES если подключена
{
  BOOL result = NO;
  if(headerPG){
    if(PQstatus(headerPG) == CONNECTION_OK)
      result = YES;
  }
  return result;
}

-(void) errorPG{ //Возбуждает исключение с сообщением об ошибке postgreSQL
  lastError = [NSString stringWithUTF8String:PQerrorMessage(headerPG)];
  mbErrorPG* e = [[mbErrorPG alloc] initWithName:@"PostgreSQLerror" reason:lastError userInfo:nil];
  @throw e;
}

-(void) dealloc
{
  @synchronized(self) {
    if(headerPG){
      PQfinish(headerPG);
    }
  }
}

- (BOOL)isBusy // YES если выполняется асинхронный запрос, NO если свободен
{
  return PQisBusy(headerPG);
}

- (void)disconnect
{
  if(headerPG){
    PQfinish(headerPG);
    headerPG = nil;
  }
}

- (NSString*) stringFromDate:(NSDate*)dt
{
  NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  [dateFormatter setDateFormat:@"yyyy-MM-dd"];
  NSString* s = [dateFormatter stringFromDate:dt];
  return s;
}
@end

Данный класс предоставляет:

    Свойства
  • headerPG - идентификатор соединения, используется внутри нашей обертки при обращении к функциям postgreSQL API. (Только для чтения).
  • serverName - URL сервера postgresSQL
  • databaseName - наименование базы данных
  • port - порт (значение по умолчанию 5432).
  • userName - логин пользователя
  • password - пароль
  • lastError - описание последней ошибки.
    Методы
  • - (id)initWithServerName:(NSString*) sName dbName:(NSString*)dbName port:(NSInteger)prt; - метод инициализации экземпляра mbDB_PG с передаваемыми параметрами для соединения,- URL сервера, наименования БД, номера порта.
  • - (id)initWithDB:(mbDB_PG*)db; - метод инициализации экземпляра mbDB_PG на основе другого объекта mbDB_PG.
  • - (void)connect; - метод осуществляет соединение с сервером и указанной БД. В случае неудачи возбуждает исключение.
  • - (void)connectToDBwithUser:(NSString*)user password:(NSString*)psw; - делает тоже, что метод connect, принимая два парамертра: имя пользователя, пароль.
  • - (BOOL)connected; - возвращает YES если соединение установлено, иначе NO.
  • -(void) errorPG; - возбуждает исключение с сообщением об ошибке postgreSQL
  • - (BOOL)isBusy; - возвращает YES если выполняется асинхронный запрос, NO если свободен.
  • - (void)disconnect; - разрывает соединение с сервером.
  • - (NSString*) stringFromDate:(NSDate*)dt; - преобразует объект NSDate в строку для запросов к БД postgreSQL в формате yyyy-MM-dd.

Теперь создаем класс, выполняющий непосредственно запросы к базе данных, также унаследованный от NSObject и назовем его mbQueryPG:

Выполнение запросов к БД, класс mbQueryPG

Листинг заголовочного файла mbQueryPG.h

#import <Foundation/Foundation.h>
#import <libpq-fe.h>

@class mbDB_PG;

@interface mbQueryPG : NSObject
{
  __weak mbDB_PG* db;
  NSString* sql;
  PGresult* pgRes;//result of query (результат запроса)
}

@property (copy) NSString* sql;
@property (weak) mbDB_PG* db;

- (id)initWithDatabase:(mbDB_PG*)database;
- (void)execQuery;//Выполнение запроса с sql
- (NSInteger)recordCount;//Кол-во записей в рез-те запроса
- (NSString*)stringValFromRow:(int)row Column:(int)col;//возвращает строковое значение из строки row и столбца col
- (NSInteger)integerValFromRow:(int)row Column:(int)col;//возвращает цело-численное значение из строки row и столбца col
- (double)doubleValFromRow:(int)row Column:(int)col;//возвращает double значение из строки row и столбца col
- (NSDate*)dateValFromRow:(int)row Column:(int)col;//возвращает NSDate значение из строки row и столбца col
- (NSString*) r_escape:(NSString*)s;//Экранирование символов в строке
- (NSString*) r_Descape:(NSData*)d; // Экранирование данных
- (NSData*) dataValFromRow:(int)row Column:(int)col; //возвращает NSData значение из строки row и столбца col 
- (int) numFields;//кол-во колонок
- (NSString*) fieldName:(int)fieldNum; //field name for column number fieldNum (наименование колонки с номером fieldNum)
- (NSArray*) fieldNames;

@end

Листинг файла реализации mbQueryPG.m

#import "mbQueryPG.h"
#import "mbDB_PG.h"

@implementation mbQueryPG

@synthesize sql, db;
- (id)initWithDatabase:(mbDB_PG*)database
{
  self = [super init];
  if (self) {
    db = database;
  }
  return self;
}

- (void)execQuery
{
  if(pgRes)
    PQclear(pgRes);//очистка предидущего результата
  pgRes = PQexec(db.headerPG, sql.UTF8String);
  if(!pgRes)//если неудача возбуждаем исключение
    [db errorPG];
  else{
    ExecStatusType status = PQresultStatus(pgRes);
    if(status == PGRES_BAD_RESPONSE || status == PGRES_NONFATAL_ERROR || status == PGRES_FATAL_ERROR)
      [db errorPG];
  }
}

- (NSInteger)recordCount
{
  NSInteger result = PQntuples(pgRes);
  return result;
}

- (NSString*)stringValFromRow:(int)row Column:(int)col
{
  NSString* ps = [[NSString alloc] initWithUTF8String:PQgetvalue(pgRes,row,col)];
  return ps;
}

- (NSInteger)integerValFromRow:(int)row Column:(int)col
{
  return [self stringValFromRow:row Column:col].integerValue;
}

- (double)doubleValFromRow:(int)row Column:(int)col
{
  return [self stringValFromRow:row Column:col].doubleValue;
}

- (NSDate*)dateValFromRow:(int)row Column:(int)col
{
  NSString* ps = [[NSString alloc] initWithUTF8String:PQgetvalue(pgRes,row,col)];
  NSDateFormatter* df = [[NSDateFormatter alloc] init];
  [df setLocale:[NSLocale currentLocale]];
  [df setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]];
  [df setDateFormat:@"yyyy-MM-dd"];
  NSDate* resDt = [df dateFromString:ps];
  return resDt;
}


- (NSString*) r_escape:(NSString*)s
{
  if(!s)
    return @"";
  NSInteger len = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  NSString* result;
  if(len){
    NSInteger l = (len<<1)+1;
    //char toch[l];
    char* to = malloc(l);//toch;
    memset(to, 0, l);
    int err = 0;
    size_t tolen = PQescapeStringConn(db.headerPG, to, s.UTF8String, len,&err);
    if(!err && tolen){
      NSString *SQL = [[NSString alloc]initWithUTF8String: to];
      result = SQL;
    }
    else
      result = s;
    free(to);
  }
  else
    result = s;
  
  return result;
}

- (NSString*) r_Descape:(NSData*)da
{
  size_t tolen;
  NSUInteger dblen = da.length;
  unsigned char* res = PQescapeByteaConn(db.headerPG,da.bytes,dblen,&tolen);
  NSString* resultS = [[NSString alloc]initWithUTF8String:(const char*)res];
  PQfreemem(res);
  return resultS;
}

- (NSData*) dataValFromRow:(int)row Column:(int)col
{
  unsigned char* val1 = (unsigned char*)PQgetvalue(pgRes,row,col);
  size_t tolen;
  unsigned char* res = PQunescapeBytea(val1, &tolen);
  NSData* result = [[NSData dataWithBytes:res length:tolen] copy];
  PQfreemem(res);
  return result;
}

- (int)numFields
{
  return PQnfields(pgRes);
}

- (NSString*) fieldName:(int)fieldNum
{
  char* resCh = PQfname(pgRes, fieldNum);
  if(!resCh)
    return @"";
  else 
    return [NSString stringWithUTF8String:resCh];
}

- (NSArray*) fieldNames
{
  NSMutableArray* result = [NSMutableArray array];
  NSInteger len=PQnfields(pgRes);
  if(len){
    for(NSInteger i=0;i<len;i++){
      [result addObject:[self fieldName:(int)i]];
    }
  }
  return [NSArray arrayWithArray:result];
}

- (void)dealloc
{
  if(pgRes)
    PQclear(pgRes);//очистка результата запроса
  
}

@end

Данный класс предоставляет:

    Свойства
  • sql - строка sql запроса к базе данных.
  • db - указатель на объект mbDB_PG.
    Методы
  • - (id)initWithDatabase:(mbDB_PG*)database; - инициализирует выделенный объект с объектом mbDB_PG.
  • - (void)execQuery; - выполнение запроса с sql.
  • - (NSInteger)recordCount; - кол-во записей (строк) в результате запроса.
  • - (NSString*)stringValFromRow:(int)row Column:(int)col; - возвращает строковое значение из строки row и столбца col.
  • - (NSInteger)integerValFromRow:(int)row Column:(int)col; - возвращает цело-численное значение из строки row и столбца col.
  • - (double)doubleValFromRow:(int)row Column:(int)col; - возвращает double значение из строки row и столбца col.
  • - (NSDate*)dateValFromRow:(int)row Column:(int)col; - возвращает NSDate значение из строки row и столбца col.
  • - (NSString*) r_escape:(NSString*)s; - экранирование символов в строке для запроса.
  • - (NSString*) r_Descape:(NSData*)d; - экранирование данных для запроса.
  • - (NSData*) dataValFromRow:(int)row Column:(int)col; - возвращает NSData значение из строки row и столбца col.
  • - (int) numFields; - кол-во колонок
  • - (NSString*) fieldName:(int)fieldNum; - возвращает наименование колонки с номером fieldNum.
  • - (NSArray*) fieldNames - возвращает массив с наименованием колонок запроса.

Выше приведенный класс выполняет синхронные запросы, т.е. пока запрос не завершен, поток, в котором он выполняется останавливается в ожидании результата запроса, чтобы выполнить асинхронный запрос,- создадим следующий класс, унаследованный от предъидущего mbQueryPG, назовем его mbAQueryPG.

Выполнение асинхронных запросов к БД, класс mbAQueryPG

Листинг заголовочного файла mbAQueryPG.h

#import "mbQueryPG.h"

@interface mbAQueryPG : mbQueryPG

- (NSInteger)fetch; // возвращает кол-во записей или -1 если запрос завершен

@end

Листинг файла реализации файла mbAQueryPG.m

#import "mbAQueryPG.h"
#import "mbDB_PG.h"

@implementation mbAQueryPG

- (void)execQuery
{
  if(pgRes)
    PQclear(pgRes);//очистка предидущего результата
  if(!PQsendQuery(db.headerPG, sql.UTF8String))
    [db errorPG];  
}

- (NSInteger)fetch
{
  NSInteger result;
  if(pgRes)
    PQclear(pgRes);//очистка предидущего результата
  pgRes = PQgetResult(db.headerPG);
  if(pgRes){
    ExecStatusType status = PQresultStatus(pgRes);
    if(status == PGRES_BAD_RESPONSE || status == PGRES_NONFATAL_ERROR || status == PGRES_FATAL_ERROR)
      [db errorPG];
    result = [self recordCount];
  }
  else 
    result = -1;
  return result;
}
@end

Данный класс переопределяет метод execQuery, который выполняет асинхронный запрос и дополняет класс mbQueryPG методом fetch, возвращающим количество считанных строк, со времени последненго его вызова, если он возвращает -1, запрос закончил свое выполнение.

Данные получаем из результата запроса, предидущие данные освобождаются автоматически при вызове данного метода.

Важно! Пока не завершен асинхронный запрос, не может быть запрошен другой, так-как для одного соединения в postgreSQL возможна только одна транзакция и только один запрос, поэтому перед вызовом асинхронного или синхронного запроса вы можете проверить состояние БД методом isBusy класса mbDB_PG.

Теперь настала очередь создать класс для обработки ошибок, унаследованный от NSException, назовем его mbErrorPG.

Обработка ошибок, класс mbErrorPG

Листинг заголовочного файла mbErrorPG.h

#import <Foundation/Foundation.h>

@interface mbErrorPG : NSException
- (void)alertBox;
@end

Листинг файла реализации файла mbErrorPG.m

#import "mbErrorPG.h"

@implementation mbErrorPG

- (void)alertBox
{
  UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"PostgreSQL"
            message:self.reason delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
  [alert show];
}

@end

Данный класс имеет всего один метод alertBox, выводящий на экран iOS устройства окно сообщений с информацией об ошибке.

В заключение создаем общий заголовочный файл, в который включаем подключение всех вышесозданных заголовочных файлов, для использования обертки в iOS проектах.

Листинг заголовочного файла mbpostgresql.h

#import "mbDB_PG.h"
#import "mbQueryPG.h"
#import "mbErrorPG.h"
#import "mbAQueryPG.h"

Из данной обертки можно создать статическую библиотеку, либо просто включать их в состав Вашего iOS проекта. При создании статической библиотеки добавьте в проект ранее созданную библиотеку libpq.a и добавьте путь поиска для файлов в папке Headers библиотеки libpq.

Файлы для скачивания:

Вышеприведенные файлы Objective-C iOS обертки для postgreSQL.

 
 
homeЗаметили ошибкукарта сайта 
   Made on a Mac