diff --git a/postgresql-kit.xcodeproj/project.pbxproj b/postgresql-kit.xcodeproj/project.pbxproj index ad99b00..98d006e 100644 --- a/postgresql-kit.xcodeproj/project.pbxproj +++ b/postgresql-kit.xcodeproj/project.pbxproj @@ -202,7 +202,7 @@ 6E48CD7D1A73FC350060B429 /* Queries.md in CopyFiles */ = {isa = PBXBuildFile; fileRef = 6E48CD7B1A73FC2B0060B429 /* Queries.md */; }; 6E4DEC87162CC38D008B26BD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6E8C168115F26BF20013A382 /* Foundation.framework */; }; 6E4DEC95162CC4AB008B26BD /* PGClientKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6ED85B4916288D0200A6DC02 /* PGClientKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 6E4DEC96162CC5E6008B26BD /* PGClientKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ED85B4616288C4800A6DC02 /* PGClientKit.framework */; }; + 6E4DEC96162CC5E6008B26BD /* PGClientKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ED85B4616288C4800A6DC02 /* PGClientKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 6E50183B1AC9BAD000D6D967 /* PGTabView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E50183A1AC9BAD000D6D967 /* PGTabView.m */; }; 6E5018411ACA90D900D6D967 /* PGTabViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E5018401ACA90D900D6D967 /* PGTabViewCell.m */; }; 6E55A2321A618C6B00CD2B60 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E55A2311A618C6B00CD2B60 /* main.m */; }; @@ -326,6 +326,17 @@ 6EFFF2351A5F04C800CAA6E7 /* PGFoundationApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EFFF2311A5F044E00CAA6E7 /* PGFoundationApp.m */; }; 6EFFF23A1A5F052A00CAA6E7 /* PGFoundationApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EFFF2311A5F044E00CAA6E7 /* PGFoundationApp.m */; }; 6EFFF23C1A5F053000CAA6E7 /* Terminal.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EFFF2391A5F051D00CAA6E7 /* Terminal.m */; }; + 74FDBBC21F32531F00606CC3 /* PGConnection+PGConnectionSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 74FDBBC01F32531F00606CC3 /* PGConnection+PGConnectionSocket.h */; }; + 74FDBBC31F32531F00606CC3 /* PGConnection+PGConnectionSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 74FDBBC01F32531F00606CC3 /* PGConnection+PGConnectionSocket.h */; }; + 74FDBBC41F32531F00606CC3 /* PGConnection+PGConnectionSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 74FDBBC11F32531F00606CC3 /* PGConnection+PGConnectionSocket.m */; }; + 74FDBBC51F32531F00606CC3 /* PGConnection+PGConnectionSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 74FDBBC11F32531F00606CC3 /* PGConnection+PGConnectionSocket.m */; }; + C6457D4E1F288DD7005F6686 /* PGConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = C6457D4A1F288DD7005F6686 /* PGConnectionOperation.m */; }; + C6457D4F1F288DD7005F6686 /* PGConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = C6457D4A1F288DD7005F6686 /* PGConnectionOperation.m */; }; + C6457D501F288DD7005F6686 /* PGConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = C6457D4A1F288DD7005F6686 /* PGConnectionOperation.m */; }; + C6457D511F288DD7005F6686 /* PGConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = C6457D4A1F288DD7005F6686 /* PGConnectionOperation.m */; }; + C6457D671F28F470005F6686 /* PGConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = C6457D4A1F288DD7005F6686 /* PGConnectionOperation.m */; }; + C6457D681F28F475005F6686 /* PGConnectionOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = C6457D491F288DD7005F6686 /* PGConnectionOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C6457D691F28F478005F6686 /* PGConnectionOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = C6457D491F288DD7005F6686 /* PGConnectionOperation.h */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -484,7 +495,7 @@ 6E4DEC83162CC38D008B26BD /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; - dstPath = /usr/share/man/man1/; + dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( ); @@ -811,7 +822,7 @@ 6EBDEA4F1AF3826A009CC810 /* PGSettingsRule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PGSettingsRule.m; sourceTree = ""; }; 6EBDEA521AF38A92009CC810 /* PGSettingsArray_testcases.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PGSettingsArray_testcases.m; sourceTree = ""; }; 6EBDEA681AF38B09009CC810 /* PGServerKit_unittests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PGServerKit_unittests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 6EBDEA691AF38B09009CC810 /* PGClientKit_unittests copy-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "PGClientKit_unittests copy-Info.plist"; path = "/Volumes/Users/djt/Projects/postgresql-kit/PGClientKit_unittests copy-Info.plist"; sourceTree = ""; }; + 6EBDEA691AF38B09009CC810 /* PGClientKit_unittests copy-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PGClientKit_unittests copy-Info.plist"; sourceTree = ""; }; 6EC5E6771AD7B55900774D57 /* PGQueryInsert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PGQueryInsert.h; sourceTree = ""; }; 6EC5E6781AD7B55900774D57 /* PGQueryInsert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PGQueryInsert.m; sourceTree = ""; }; 6EC5E67E1AD7BDDE00774D57 /* PGQueryUpdate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PGQueryUpdate.h; sourceTree = ""; }; @@ -846,6 +857,10 @@ 6EFFF2331A5F044E00CAA6E7 /* PGFoundationServer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PGFoundationServer.m; sourceTree = ""; }; 6EFFF2381A5F051D00CAA6E7 /* Terminal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Terminal.h; sourceTree = ""; }; 6EFFF2391A5F051D00CAA6E7 /* Terminal.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Terminal.m; sourceTree = ""; }; + 74FDBBC01F32531F00606CC3 /* PGConnection+PGConnectionSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PGConnection+PGConnectionSocket.h"; sourceTree = ""; }; + 74FDBBC11F32531F00606CC3 /* PGConnection+PGConnectionSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "PGConnection+PGConnectionSocket.m"; sourceTree = ""; }; + C6457D491F288DD7005F6686 /* PGConnectionOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PGConnectionOperation.h; path = src/Frameworks/PGClientKit/PGConnectionOperation.h; sourceTree = SOURCE_ROOT; }; + C6457D4A1F288DD7005F6686 /* PGConnectionOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PGConnectionOperation.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1588,6 +1603,8 @@ 6E9256B61AA64BDA007DAD82 /* PGConnection+Notifications.m */, 6E9256B71AA64BDA007DAD82 /* PGConnection+Ping.m */, 6E9256B81AA64BDA007DAD82 /* PGConnection+Reset.m */, + 74FDBBC01F32531F00606CC3 /* PGConnection+PGConnectionSocket.h */, + 74FDBBC11F32531F00606CC3 /* PGConnection+PGConnectionSocket.m */, ); name = PGConnection; sourceTree = ""; @@ -1694,6 +1711,8 @@ 6ED85B3316288BEF00A6DC02 /* PGClientKit */ = { isa = PBXGroup; children = ( + C6457D491F288DD7005F6686 /* PGConnectionOperation.h */, + C6457D4A1F288DD7005F6686 /* PGConnectionOperation.m */, 1413DF1D17AD79AC009615E5 /* PGClientKit_ios.plist */, 1413DF1E17AD79AC009615E5 /* PGClientKit_mac.plist */, 6ED85B4916288D0200A6DC02 /* PGClientKit.h */, @@ -1809,11 +1828,13 @@ 6E0DDCF61AAB74B100A38F3B /* PGQueryTableView.h in Headers */, 6EC6748E1AD94FC700CAB4FF /* NSError+PGAdditions.h in Headers */, 6EC674871AD94F6100CAB4FF /* PGQueryInsert.h in Headers */, + C6457D691F28F478005F6686 /* PGConnectionOperation.h in Headers */, 1450A1681AAA18DA002D62D9 /* PGQueryPredicate.h in Headers */, 6E1548441A5819AC00556573 /* PGClientKit.h in Headers */, 1450A15F1AAA145A002D62D9 /* PGQuerySource.h in Headers */, 6E15484D1A5819EA00556573 /* PGQuery.h in Headers */, 6EC6748B1AD94F8900CAB4FF /* PGConnectionPool.h in Headers */, + 74FDBBC31F32531F00606CC3 /* PGConnection+PGConnectionSocket.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1834,6 +1855,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + C6457D681F28F475005F6686 /* PGConnectionOperation.h in Headers */, 6E4DEC95162CC4AB008B26BD /* PGClientKit.h in Headers */, 6E0DDCE31AAB6F5200A38F3B /* PGQueryDatabase.h in Headers */, 149BA1A41AA6FB54007B86A0 /* PGQuerySelect.h in Headers */, @@ -1854,6 +1876,7 @@ 6E4016B91ABEFA09009A2707 /* PGTransaction.h in Headers */, 1489AEC716669A6B00C3B67D /* PGResult+TextTable.h in Headers */, 14DECDA816B57EC400178235 /* PGConverters+Private.h in Headers */, + 74FDBBC21F32531F00606CC3 /* PGConnection+PGConnectionSocket.h in Headers */, 6EC5E6801AD7BDDE00774D57 /* PGQueryUpdate.h in Headers */, 1417B7BA1AAA1FF200B44DD4 /* PGQueryObject.h in Headers */, 6E0DDCD91AAB2A4000A38F3B /* NSString+PrivateAdditions.h in Headers */, @@ -2395,6 +2418,7 @@ 6E9256B31AA64B6C007DAD82 /* PGConnection+Connect.m in Sources */, 6E9256A21AA645FC007DAD82 /* PGConnection.m in Sources */, 1450A16A1AAA18DA002D62D9 /* PGQueryPredicate.m in Sources */, + 74FDBBC51F32531F00606CC3 /* PGConnection+PGConnectionSocket.m in Sources */, 6E1548461A5819C600556573 /* NSURL+PGAdditions.m in Sources */, 6EC6748D1AD94FAC00CAB4FF /* PGTransaction.m in Sources */, 6E0DDCE61AAB6F5200A38F3B /* PGQueryDatabase.m in Sources */, @@ -2413,6 +2437,7 @@ 6E15484E1A5819F000556573 /* PGQuery.m in Sources */, 6EC6748F1AD94FCC00CAB4FF /* NSError+PGAdditions.m in Sources */, 6E9256BC1AA64BDA007DAD82 /* PGConnection+Execute.m in Sources */, + C6457D4E1F288DD7005F6686 /* PGConnectionOperation.m in Sources */, 6E1548581A581F2B00556573 /* SSKeychain.m in Sources */, 6E0DDCF81AAB74B100A38F3B /* PGQueryTableView.m in Sources */, ); @@ -2432,6 +2457,7 @@ files = ( 14FD07261AA711210013557E /* GBCommandLineParser.m in Sources */, 6EFFF23C1A5F053000CAA6E7 /* Terminal.m in Sources */, + C6457D501F288DD7005F6686 /* PGConnectionOperation.m in Sources */, 14FD07251AA7111E0013557E /* GBOptionsHelper.m in Sources */, 14FD07231AA711140013557E /* GBSettings.m in Sources */, 6EFFF23A1A5F052A00CAA6E7 /* PGFoundationApp.m in Sources */, @@ -2446,6 +2472,7 @@ files = ( 6E6154D01AC6F84B00541DDE /* LogController.m in Sources */, 6E55A2321A618C6B00CD2B60 /* main.m in Sources */, + C6457D511F288DD7005F6686 /* PGConnectionOperation.m in Sources */, 6E55A2341A618CD300CD2B60 /* Application.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2478,6 +2505,7 @@ 6EFFF2341A5F04C500CAA6E7 /* PGFoundationServer.m in Sources */, 14C245E51AB6C770005ED153 /* GBCommandLineParser.m in Sources */, 14C245DF1AB6C766005ED153 /* GBSettings.m in Sources */, + C6457D4F1F288DD7005F6686 /* PGConnectionOperation.m in Sources */, 14C245E31AB6C76A005ED153 /* GBPrint.m in Sources */, 6EFFF2351A5F04C800CAA6E7 /* PGFoundationApp.m in Sources */, 14C245E41AB6C76D005ED153 /* GBOptionsHelper.m in Sources */, @@ -2516,6 +2544,7 @@ 6E9256A81AA6481E007DAD82 /* PGConnection+Callbacks.m in Sources */, 6E9256BD1AA64BDA007DAD82 /* PGConnection+Notifications.m in Sources */, 1489AEC816669A6B00C3B67D /* PGResult+TextTable.m in Sources */, + 74FDBBC41F32531F00606CC3 /* PGConnection+PGConnectionSocket.m in Sources */, 6E9256B21AA64B6C007DAD82 /* PGConnection+Connect.m in Sources */, 6E308D061ABD80810091BD17 /* NSError+PGAdditions.m in Sources */, 6E82F1741A9BC9B20024B8BE /* PGConnection.m in Sources */, @@ -2524,6 +2553,7 @@ 6E0DDCEB1AAB721600A38F3B /* PGQueryRole.m in Sources */, 1450A1691AAA18DA002D62D9 /* PGQueryPredicate.m in Sources */, 6E0DDCE51AAB6F5200A38F3B /* PGQueryDatabase.m in Sources */, + C6457D671F28F470005F6686 /* PGConnectionOperation.m in Sources */, 14DECDA616B56DF300178235 /* PGConverters+Data2Object.m in Sources */, 6EAA40ED17BC150E0078EB32 /* NSURL+PGAdditions.m in Sources */, 147070E31A976E230069EF9C /* NSString+PGNetworkValidationAdditions.m in Sources */, @@ -2836,10 +2866,14 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_ARC = YES; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG2=0", + "DEBUG=0", + ); ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx10.9; + OTHER_CFLAGS = "-I/usr/local/include"; + SDKROOT = macosx10.10; STRIP_INSTALLED_PRODUCT = NO; }; name = Debug; @@ -2848,7 +2882,9 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_ARC = YES; - SDKROOT = macosx10.9; + GCC_OPTIMIZATION_LEVEL = s; + OTHER_CFLAGS = "-I/usr/local/include"; + SDKROOT = macosx10.10; }; name = Release; }; @@ -2900,6 +2936,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; + LD_RUNPATH_SEARCH_PATHS = "${BUILT_PRODUCTS_DIR}"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -2917,6 +2954,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; + LD_RUNPATH_SEARCH_PATHS = "${BUILT_PRODUCTS_DIR}"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; diff --git a/postgresql-kit.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/postgresql-kit.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 3e4714f..94b2795 100644 --- a/postgresql-kit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/postgresql-kit.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -1,7 +1,4 @@ - - diff --git a/src/Apps/Foundation/PGFoundationClient.h b/src/Apps/Foundation/PGFoundationClient.h index 08057d3..0451c3a 100644 --- a/src/Apps/Foundation/PGFoundationClient.h +++ b/src/Apps/Foundation/PGFoundationClient.h @@ -19,9 +19,9 @@ #import "Terminal.h" @interface PGFoundationClient : PGFoundationApp { - PGConnection* _db; - PGPasswordStore* _passwordstore; - Terminal* _term; + // PGConnection* _db; + // PGPasswordStore* _passwordstore; + // Terminal* _term; } // properties diff --git a/src/Apps/Foundation/PGFoundationClient.m b/src/Apps/Foundation/PGFoundationClient.m index 14eb8b1..0769398 100644 --- a/src/Apps/Foundation/PGFoundationClient.m +++ b/src/Apps/Foundation/PGFoundationClient.m @@ -1,4 +1,4 @@ - + // Copyright 2009-2015 David Thorpe // https://github.com/djthorpe/postgresql-kit // @@ -15,7 +15,9 @@ #import "PGFoundationClient.h" @implementation PGFoundationClient - +@synthesize db = _db; +@synthesize term = _term; +@synthesize passwordstore = _passwordstore; //////////////////////////////////////////////////////////////////////////////// // constructor @@ -36,9 +38,7 @@ -(id)init { //////////////////////////////////////////////////////////////////////////////// // properties -@synthesize db = _db; -@synthesize term = _term; -@synthesize passwordstore = _passwordstore; + @dynamic url; @dynamic prompt; @@ -69,7 +69,7 @@ -(NSString* )prompt { // PGConnectionDelegate delegate implementation -(void)connection:(PGConnection* )connection willOpenWithParameters:(NSMutableDictionary* )dictionary { -#ifdef DEBUG2 +#if defined(DEBUG2) && DEBUG2 == 1 [[self term] printf:@"connection:willOpenWithParameters:%@",dictionary]; #endif // if there is a password in the parameters, then store it @@ -83,12 +83,13 @@ -(void)connection:(PGConnection* )connection willOpenWithParameters:(NSMutableDi } } --(void)connection:(PGConnection* )connection willExecute:(NSString *)query { +-(NSString* )connection:(PGConnection* )connection willExecute:(NSString *)query { [[self term] printf:query]; + return nil; } -(void)connection:(PGConnection* )connection statusChange:(PGConnectionStatus)status description:(NSString *)description { -#ifdef DEBUG2 +#if defined(DEBUG2) && DEBUG2 == 1 [[self term] printf:@"StatusChange: %@ (%d)",description,status]; #endif // disconnected @@ -127,13 +128,18 @@ -(void)connect:(NSURL* )url { } -(void)execute:(id)query { - [[self db] executeQuery:query whenDone:^(PGResult* result, NSError* error) { + + +// query = [PGQuery queryWithString:@"SELECT datname FROM pg_database"]; + + + [[self db] execute:query whenDone:^(PGResult* result, NSError* error) { if(error) { [[self term] printf:@"Error: %@ (%@/%ld)",[error localizedDescription],[error domain],[error code]]; } if(result) { [self displayResult:result]; - [[self term] addHistory:query]; + [[self term] addHistory: query ]; } }]; } @@ -183,7 +189,7 @@ -(void)command:(NSString* )command args:(NSArray* )args { [[self term] printf:@"error: tables: too many arguments"]; } else { PGQueryObject* query = [PGQuery queryWithString:@"SELECT datname AS database,pid AS pid,query AS query,usename AS username,client_hostname AS remotehost,application_name,query_start,waiting FROM pg_stat_activity WHERE pid <> pg_backend_pid()"]; - [[self db] executeQuery:query whenDone:^(PGResult* result, NSError* error) { + [[self db] execute:query whenDone:^(PGResult* result, NSError* error) { if(result) { [self displayResult:result]; } @@ -218,9 +224,9 @@ -(void)command:(NSString* )command args:(NSArray* )args { [[self term] printf:@"error: table: not enough arguments"]; return; } - PGQueryObject* query = [PGQuerySelect select:[PGQuerySource sourceWithTable:tableName schema:schemaName alias:nil] options:0]; + PGQueryObject* query = [PGQuerySelect select:[PGQuerySource table:tableName schema:schemaName alias:nil] options:0]; NSParameterAssert(query); - [[self db] executeQuery:query whenDone:^(PGResult* result, NSError* error) { + [[self db] execute:query whenDone:^(PGResult* result, NSError* error) { if(result) { [self displayResult:result]; } @@ -241,7 +247,7 @@ -(void)command:(NSString* )command args:(NSArray* )args { } PGQuery* query = [PGQueryRole create:roleName options:0]; NSParameterAssert(query); - [[self db] executeQuery:query whenDone:^(PGResult* result, NSError* error) { + [[self db] execute:query whenDone:^(PGResult* result, NSError* error) { if(result) { [self displayResult:result]; } @@ -262,7 +268,7 @@ -(void)command:(NSString* )command args:(NSArray* )args { } PGQuery* query = [PGQueryRole drop:roleName options:0]; NSParameterAssert(query); - [[self db] executeQuery:query whenDone:^(PGResult* result, NSError* error) { + [[self db] execute:query whenDone:^(PGResult* result, NSError* error) { if(result) { [self displayResult:result]; } @@ -283,7 +289,7 @@ -(void)command:(NSString* )command args:(NSArray* )args { } PGQuery* query = [PGQueryDatabase create:databaseName options:0]; NSParameterAssert(query); - [[self db] executeQuery:query whenDone:^(PGResult* result, NSError* error) { + [[self db] execute:query whenDone:^(PGResult* result, NSError* error) { if(result) { [self displayResult:result]; } @@ -304,7 +310,7 @@ -(void)command:(NSString* )command args:(NSArray* )args { } PGQuery* query = [PGQueryDatabase drop:databaseName options:0]; NSParameterAssert(query); - [[self db] executeQuery:query whenDone:^(PGResult* result, NSError* error) { + [[self db] execute:query whenDone:^(PGResult* result, NSError* error) { if(result) { [self displayResult:result]; } @@ -325,7 +331,7 @@ -(void)command:(NSString* )command args:(NSArray* )args { } PGQuery* query = [PGQuerySchema create:schemaName options:0]; NSParameterAssert(query); - [[self db] executeQuery:query whenDone:^(PGResult* result, NSError* error) { + [[self db] execute:query whenDone:^(PGResult* result, NSError* error) { if(result) { [self displayResult:result]; } @@ -346,7 +352,7 @@ -(void)command:(NSString* )command args:(NSArray* )args { } PGQuery* query = [PGQuerySchema drop:schemaName options:0]; NSParameterAssert(query); - [[self db] executeQuery:query whenDone:^(PGResult* result, NSError* error) { + [[self db] execute:query whenDone:^(PGResult* result, NSError* error) { if(result) { [self displayResult:result]; } @@ -360,7 +366,7 @@ -(void)command:(NSString* )command args:(NSArray* )args { if([command isEqualToString:@"listschemas"]) { PGQuery* query = [PGQuerySchema listWithOptions:0]; NSParameterAssert(query); - [[self db] executeQuery:query whenDone:^(PGResult* result, NSError* error) { + [[self db] execute:query whenDone:^(PGResult* result, NSError* error) { if(result) { [self displayResult:result]; } @@ -374,7 +380,7 @@ -(void)command:(NSString* )command args:(NSArray* )args { if([command isEqualToString:@"listroles"]) { PGQuery* query = [PGQueryRole listWithOptions:0]; NSParameterAssert(query); - [[self db] executeQuery:query whenDone:^(PGResult* result, NSError* error) { + [[self db] execute:query whenDone:^(PGResult* result, NSError* error) { if(result) { [self displayResult:result]; } @@ -392,7 +398,7 @@ -(void)command:(NSString* )command args:(NSArray* )args { [query addColumn:@"y" alias:@"col2"]; NSParameterAssert(query); - [[self db] executeQuery:query whenDone:^(PGResult* result, NSError* error) { + [[self db] execute:query whenDone:^(PGResult* result, NSError* error) { if(result) { [self displayResult:result]; } diff --git a/src/Frameworks/PGClientKit/NSURL+PGAdditions.m b/src/Frameworks/PGClientKit/NSURL+PGAdditions.m index fa0e79f..b86c1b2 100644 --- a/src/Frameworks/PGClientKit/NSURL+PGAdditions.m +++ b/src/Frameworks/PGClientKit/NSURL+PGAdditions.m @@ -21,7 +21,13 @@ @implementation NSURL (PGAdditions) // PRIVATE METHODS +(NSString* )_pg_urlencode:(NSString* )string { - return (NSString* )CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,(__bridge CFStringRef)string,NULL,(__bridge CFStringRef)@"!*'();:@&=+$,/?%#[]",kCFStringEncodingUTF8)); +#if ( defined(__IPHONE_10_3) && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3 ) || ( defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_12 ) +// NSCharacterSet *allowedCharset = [NSCharacterSet characterSetWithCharactersInString:@"!*'();:@&=+$,/?%#[]"]; + NSCharacterSet *allowedCharset = [NSCharacterSet URLQueryAllowedCharacterSet ]; + return (NSString* ) [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharset ] ; +#else + return (NSString* )CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,(__bridge CFStringRef)string,NULL,(__bridge CFStringRef)@"!*'();:@&=+$,/?%#[]",kCFStringEncodingUTF8)); +#endif } +(NSString* )_pg_urlencode_params:(NSDictionary* )params { @@ -288,8 +294,17 @@ -(NSDictionary* )postgresqlParameters { // we require a key/value pair for any additional parameter return nil; } - NSString* theKey = [[theKeyValue objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - NSString* theValue = [[theKeyValue objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + +#if ( defined(__IPHONE_10_3) && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3 ) || ( defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_12 ) + + NSCharacterSet *allowedCharset = [NSCharacterSet URLPathAllowedCharacterSet]; + + NSString* theKey = [[theKeyValue objectAtIndex:0] stringByAddingPercentEncodingWithAllowedCharacters: allowedCharset]; + NSString* theValue = [[theKeyValue objectAtIndex:1] stringByAddingPercentEncodingWithAllowedCharacters: allowedCharset]; +#else + NSString* theKey = [[theKeyValue objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString* theValue = [[theKeyValue objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; +#endif // insert into theParameters, allow override of sslmode if([theParameters objectForKey:theKey]==nil || [theKey isEqual:@"sslmode"]) { diff --git a/src/Frameworks/PGClientKit/PGClientKit+Private.h b/src/Frameworks/PGClientKit/PGClientKit+Private.h index d6b32d1..788af96 100644 --- a/src/Frameworks/PGClientKit/PGClientKit+Private.h +++ b/src/Frameworks/PGClientKit/PGClientKit+Private.h @@ -63,7 +63,7 @@ PGKVPairs* makeKVPairs(NSDictionary* dict); void freeKVPairs(PGKVPairs* pairs); // profiling macros -#ifdef DEBUG2 +#if defined(DEBUG2) && DEBUG2 == 1 #include #define TIME_TICK(name) NSLog(@"TICK: %@",(name)); \ uint64_t tick_time = mach_absolute_time(); \ diff --git a/src/Frameworks/PGClientKit/PGClientKit.h b/src/Frameworks/PGClientKit/PGClientKit.h index d4697c4..6949a68 100644 --- a/src/Frameworks/PGClientKit/PGClientKit.h +++ b/src/Frameworks/PGClientKit/PGClientKit.h @@ -13,6 +13,156 @@ // under the License. #import +#import + + +enum { // Legal level values for CFLog() + kCFLogLevelEmergency = 0, + kCFLogLevelAlert = 1, + kCFLogLevelCritical = 2, + kCFLogLevelError = 3, + kCFLogLevelWarning = 4, + kCFLogLevelNotice = 5, + kCFLogLevelInfo = 6, + kCFLogLevelDebug = 7, +}; + + +enum { + kCFSocketStateReady = 0, + kCFSocketStateInvalidating = 1, + kCFSocketStateInvalid = 2, + kCFSocketStateDeallocating = 3 +}; + +typedef struct __CFRuntimeBase { + uintptr_t _cfisa; + uint8_t _cfinfo[4]; +#if __LP64__ + uint32_t _rc; +#endif +} CFRuntimeBase; + + +struct __shared_blob { + __unsafe_unretained dispatch_source_t _rdsrc; + __unsafe_unretained dispatch_source_t _wrsrc; + CFRunLoopSourceRef _source; + CFSocketNativeHandle _socket; + uint8_t _closeFD; + uint8_t _refCnt; +}; + +struct __CFSocket { + CFRuntimeBase _base; + struct __shared_blob *_shared; // non-NULL when valid, NULL when invalid + + uint8_t _state:2; // mutable, not written safely + uint8_t _isSaneFD:1; // immutable + uint8_t _connOriented:1; // immutable + uint8_t _wantConnect:1; // immutable + uint8_t _wantWrite:1; // immutable + uint8_t _wantReadType:2; // immutable + + uint8_t _error; + + uint8_t _rsuspended:1; + uint8_t _wsuspended:1; + uint8_t _readable:1; + uint8_t _writeable:1; + uint8_t _unused:4; + + uint8_t _reenableRead:1; + uint8_t _readDisabled:1; + uint8_t _reenableWrite:1; + uint8_t _writeDisabled:1; + uint8_t _connectDisabled:1; + uint8_t _connected:1; + uint8_t _leaveErrors:1; + uint8_t _closeOnInvalidate:1; + + int32_t _runLoopCounter; + + CFDataRef _address; // immutable, once created + CFDataRef _peerAddress; // immutable, once created + CFSocketCallBack _callout; // immutable + CFSocketContext _context; // immutable +} ; + +// +//struct __CFCF_CFSocket { +// CFRuntimeBase _base; +// struct { +// unsigned client:8; // flags set by client (reenable, CloseOnInvalidate) +// unsigned disabled:8; // flags marking disabled callbacks +// unsigned connected:1; // Are we connected yet? (also true for connectionless sockets) +// unsigned writableHint:1; // Did the polling the socket show it to be writable? +// unsigned closeSignaled:1; // Have we seen FD_CLOSE? (only used on Win32) +// unsigned unused:13; +// } _f; +// SInt32 _lock; +// SInt32 _writeLock; +// CFSocketNativeHandle _socket; /* immutable */ +// SInt32 _socketType; +// SInt32 _errorCode; +// CFDataRef _address; +// CFDataRef _peerAddress; +// SInt32 _socketSetCount; +// CFRunLoopSourceRef _source0; // v0 RLS, messaged from SocketMgr +// CFMutableArrayRef _runLoops; +// CFSocketCallBack _callout; /* immutable */ +// CFSocketContext _context; /* immutable */ +// CFMutableArrayRef _dataQueue; // queues to pass data from SocketMgr thread +// CFMutableArrayRef _addressQueue; +// +// struct timeval _readBufferTimeout; +// CFMutableDataRef _readBuffer; +// CFIndex _bytesToBuffer; /* is length of _readBuffer */ +// CFIndex _bytesToBufferPos; /* where the next _CFSocketRead starts from */ +// CFIndex _bytesToBufferReadPos; /* Where the buffer will next be read into (always after _bytesToBufferPos, but less than _bytesToBuffer) */ +// Boolean _atEOF; +// int _bufferedReadError; +// +// CFMutableDataRef _leftoverBytes; +// +// // +// // If the timeout is set on the CFSocketRef but we never get select() timeout +// // because we always have some network events so select never times out (e.g. while having a large download). +// // We need to notify any waiting buffered read clients if there is data available without relying on select timing out. +// struct timeval _readBufferTimeoutNotificationTime; +// Boolean _hitTheTimeout; +//}; +typedef struct __CFSocket * __CF_CFSocketRef; +typedef CFStringRef CFRunLoopMode ; +// CF_EXTENSIBLE_STRING_ENUM; +struct __CFRunLoop { + CFRuntimeBase _base; + pthread_mutex_t _lock; /* locked for accessing mode list */ + void * _wakeUpPort; // used for CFRunLoopWakeUp + Boolean _ignoreWakeUps; + volatile uint32_t *_stopped; + pthread_t _pthread; + uint32_t _winthread; + CFMutableSetRef _commonModes; + CFMutableSetRef _commonModeItems; + CFRunLoopMode * _currentMode; + CFMutableSetRef _modes; + struct _block_item *_blocks_head; + struct _block_item *_blocks_tail; + CFTypeRef _counterpart; +}; + +struct __CFRunLoopSource { + CFRuntimeBase _base; + uint32_t _bits; + pthread_mutex_t _lock; + CFIndex _order; /* immutable */ + CFMutableBagRef _runLoops; + union { + CFRunLoopSourceContext version0; /* immutable, except invalidation */ + CFRunLoopSourceContext1 version1; /* immutable, except invalidation */ + } _context; +}; //////////////////////////////////////////////////////////////////////////////// @@ -65,6 +215,9 @@ typedef enum { #import "PGConnection.h" #import "PGConnectionPool.h" +// queries callback pool +#import "PGConnectionOperation.h" + // queries #import "PGQueryObject.h" #import "PGQuery.h" diff --git a/src/Frameworks/PGClientKit/PGConnection+Callbacks.m b/src/Frameworks/PGClientKit/PGConnection+Callbacks.m index 9e1e558..c2ac6d1 100644 --- a/src/Frameworks/PGClientKit/PGConnection+Callbacks.m +++ b/src/Frameworks/PGClientKit/PGConnection+Callbacks.m @@ -12,19 +12,58 @@ // License for the specific language governing permissions and limitations // under the License. +// ************************************* +// +// Copyright 2017 - ?? Sebastien Cotillard - Genose.org +// 07/2017 Sebastien Cotillard +// https://github.com/genose +// +// ************************************* +// ADDING Pool concurrent operation +// ************************************* + #import #import +#include //////////////////////////////////////////////////////////////////////////////// #pragma mark C callback functions //////////////////////////////////////////////////////////////////////////////// + +//typedef struct __shared_blob soctted ; /** * This method is called from the run loop upon new data being available to read * on the socket, or the socket being able to write more data to the socket */ -void _socketCallback(CFSocketRef s, CFSocketCallBackType callBackType,CFDataRef address,const void* data,void* self) { - [(__bridge PGConnection* )self _socketCallback:callBackType]; +static int socketUsed_in = 0; +void _socketCallback(CFSocketRef s, CFSocketCallBackType callBackType,CFDataRef address,const void* data,void* __self) { + + + + // [NSThread sleepForTimeInterval:0.01]; + + PGConnection* connection = (PGConnection* ) ((__bridge PGConnection* )__self); + if(! connection + + || ! [ (PGConnection*)connection masterPoolOperation] + // || socketUsed_in > 20 + ) + return ; + socketUsed_in ++; + // dispatch_barrier_async(dispatch_get_main_queue(), ^{ +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"%@ :: %s :::: Socket CALL ... %lu ", NSStringFromClass([((__bridge NSObject *)__self) class]), (__FUNCTION__), callBackType); +#endif + [((PGConnection* )connection) _socketCallback:callBackType]; +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"%@ :: %s :::: Socket CALL END .... (%lu) ", NSStringFromClass([((__bridge NSObject *)__self) class]), (__FUNCTION__), callBackType); +#endif + socketUsed_in = 0; + // }); + + // [NSThread sleepForTimeInterval:.1];; + } /** @@ -32,118 +71,237 @@ void _socketCallback(CFSocketRef s, CFSocketCallBackType callBackType,CFDataRef * the libpq library */ void _noticeProcessor(void* arg,const char* cString) { - NSString* notice = [NSString stringWithUTF8String:cString]; - PGConnection* connection = (__bridge PGConnection* )arg; - NSCParameterAssert(connection && [connection isKindOfClass:[PGConnection class]]); - if([[connection delegate] respondsToSelector:@selector(connection:notice:)]) { - [[connection delegate] connection:connection notice:notice]; - } + NSString* notice = [NSString stringWithUTF8String:cString]; + PGConnection* connection = (__bridge PGConnection* )arg; + NSCParameterAssert(connection && [connection isKindOfClass:[PGConnection class]]); + if([[connection delegate] respondsToSelector:@selector(connection:notice:)]) { + [[connection delegate] connection:connection notice:notice]; + } } @implementation PGConnection (Callbacks) -//////////////////////////////////////////////////////////////////////////////// -#pragma mark private methods - socket connect/disconnect -//////////////////////////////////////////////////////////////////////////////// - --(void)_socketConnect:(PGConnectionState)state { - NSParameterAssert(_state==PGConnectionStateNone); - NSParameterAssert(state==PGConnectionStateConnect || state==PGConnectionStateReset || state==PGConnectionStateNone); - NSParameterAssert(_connection); - NSParameterAssert(_socket==nil && _runloopsource==nil); - - // create socket object - CFSocketContext context = {0, (__bridge void* )(self), NULL, NULL, NULL}; - _socket = CFSocketCreateWithNative(NULL,PQsocket(_connection),kCFSocketReadCallBack | kCFSocketWriteCallBack,&_socketCallback,&context); - NSParameterAssert(_socket && CFSocketIsValid(_socket)); - // let libpq do the socket closing - CFSocketSetSocketFlags(_socket,~kCFSocketCloseOnInvalidate & CFSocketGetSocketFlags(_socket)); - - // set state - [self setState:state]; - [self _updateStatus]; - - // add to run loop to begin polling - _runloopsource = CFSocketCreateRunLoopSource(NULL,_socket,0); - NSParameterAssert(_runloopsource && CFRunLoopSourceIsValid(_runloopsource)); - CFRunLoopAddSource(CFRunLoopGetCurrent(),_runloopsource,(CFStringRef)kCFRunLoopCommonModes); -} - --(void)_socketDisconnect { - if(_runloopsource) { - CFRunLoopSourceInvalidate(_runloopsource); - CFRelease(_runloopsource); - _runloopsource = nil; - } - if(_socket) { - CFSocketInvalidate(_socket); - CFRelease(_socket); - _socket = nil; - } -} - //////////////////////////////////////////////////////////////////////////////// #pragma mark private methods - socket callbacks //////////////////////////////////////////////////////////////////////////////// -(void)_socketCallbackNotification { - NSParameterAssert(_connection); - // consume input - PQconsumeInput(_connection); - // loop for notifications - PGnotify* notify = nil; - while((notify = PQnotifies(_connection)) != nil) { - if([[self delegate] respondsToSelector:@selector(connection:notificationOnChannel:payload:)]) { - NSString* channel = [NSString stringWithUTF8String:notify->relname]; - NSString* payload = [NSString stringWithUTF8String:notify->extra]; - [[self delegate] connection:self notificationOnChannel:channel payload:payload]; + @try + { + // NSParameterAssert(_connection); + if(!_connection){ + + return; } - PQfreemem(notify); - } + // consume input + PQconsumeInput(_connection); + +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read - _socketCallbackNotification :: loop "); +#endif + + // loop for notifications + PGnotify* notify = nil; + while((notify = PQnotifies(_connection)) != nil) { + if([[self delegate] respondsToSelector:@selector(connection:notificationOnChannel:payload:)]) { +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read - _socketCallbackNotification :: call delegate (%@) (%@)", [self delegate], NSStringFromSelector(@selector(connection:notificationOnChannel:payload:))); +#endif + + NSString* channel = [NSString stringWithUTF8String:notify->relname]; + NSString* payload = [NSString stringWithUTF8String:notify->extra]; + [[self delegate] connection:self notificationOnChannel:channel payload:payload]; + } + PQfreemem(notify); + } +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read - _socketCallbackNotification :: free "); +#endif + } + @catch (NSException *exception) { + NSLog(@" %@ exeception .... %@",NSStringFromSelector(_cmd),exception); + } + @finally { + + } } -(void)_socketCallbackConnectEndedWithStatus:(PostgresPollingStatusType)pqstatus { - NSParameterAssert(_callback); - void (^callback)(BOOL usedPassword,NSError* error) = (__bridge void (^)(BOOL,NSError* ))(_callback); - - BOOL needsPassword = PQconnectionNeedsPassword(_connection) ? YES : NO; - BOOL usedPassword = PQconnectionUsedPassword(_connection) ? YES : NO; - - // update the status - [self setState:PGConnectionStateNone]; - [self _updateStatus]; // this also calls disconnect when rejected - - // callback - if(pqstatus==PGRES_POLLING_OK) { - // set up notice processor, set success condition - PQsetNoticeProcessor(_connection,_noticeProcessor,(__bridge void *)(self)); - callback(usedPassword ? YES : NO,nil); - } else if(needsPassword) { - // error callback - connection not made, needs password - callback(NO,[self raiseError:nil code:PGClientErrorNeedsPassword]); - } else if(usedPassword) { - // error callback - connection not made, password was invalid - callback(YES,[self raiseError:nil code:PGClientErrorInvalidPassword]); - } else { - // error callback - connection not made, some other kind of rejection - callback(YES,[self raiseError:nil code:PGClientErrorRejected]); - } - _callback = nil; + + //:: void (^callback)(BOOL usedPassword,NSError* error) = (__bridge void (^)(BOOL,NSError* ))(_callback); + PGConnectionOperation* currentPool = ((PGConnectionOperation*)[self currentPoolOperation]); + if( + !_connection || + !currentPool || + ![currentPool valid] || + [currentPool poolIdentifier] != 0) + return; + + pqstatus = PQconnectPoll(_connection); + + + + BOOL needsPassword = PQconnectionNeedsPassword(_connection) ? YES : NO; + BOOL usedPassword = PQconnectionUsedPassword(_connection) ? YES : NO; +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"%@ (%p) :: BEGIN :: - Read::BEGIN - %@ :: free (callback %p)", NSStringFromClass([self class]), self, NSStringFromSelector(_cmd), callback); +#endif + // update the status + [self setState:PGConnectionStateNone]; + [self _updateStatus]; // this also calls disconnect when rejected + + @try { + // callback + + + _callbackOperation = [((PGConnectionOperation*)[self masterPoolOperation]) getCallback]; + void (^callback)(BOOL usedPassword,NSError* error ) = nil; + if(_callbackOperation != nil) + { + callback = (__bridge void (^)( BOOL,NSError* ))( _callbackOperation ); + + }else{ + [NSException raise:NSInvalidArgumentException format:@" Warning :: Master Pool callback was sudently cleaned .... "]; + } + + + + if(pqstatus==PGRES_POLLING_OK) { + // set up notice processor, set success condition + PQsetNoticeProcessor(_connection,_noticeProcessor,(__bridge void *)(self)); + currentPool = [self currentPoolOperation]; + if(!currentPool) { + + return; + } + + if([currentPool poolIdentifier] == 0){ + + + mach_port_t machTID = pthread_mach_thread_np(pthread_self()); + + const char * queued_name = [[NSString stringWithFormat:@"%s_%x_%@", "operation_dispacthed_threads", machTID, NSStringFromSelector(_cmd) ] cString]; +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" //// %s ", queued_name); +#endif + dispatch_queue_t queue_inRun = dispatch_queue_create(queued_name, DISPATCH_QUEUE_CONCURRENT); + queue_inRun = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); + + + + + [((PGConnectionOperation*)[self masterPoolOperation]) invalidate]; + + + if(!_connection) + { + return; + } + + __block dispatch_semaphore_t master_semaphore_lock = nil; + + dispatch_async(queue_inRun,^{ + @try{ + + master_semaphore_lock = [((PGConnectionOperation*)[self masterPoolOperation]) semaphore]; // dispatch_semaphore_create(0); + _callbackOperation = [((PGConnectionOperation*)[self masterPoolOperation]) getCallback]; + if(_callbackOperation != nil && _connection ) + { + void (^callback_master)(BOOL usedPassword,NSError* error ) = (__bridge void (^)( BOOL,NSError* ))( _callbackOperation ); + + callback_master(usedPassword ? YES : NO,nil); + + }else if(!_connection && !_connectionClosed){ + [NSException raise:NSInvalidArgumentException format:@" Warning :: Master Pool (%p) callback (async) was sudently cleaned .... ", self]; + } + else if( ! _connectionClosed ){ + [NSException raise:NSInvalidArgumentException format:@" Warning :: Master Pool (%p) Warning (async) :: NO CONNECTION :: was sudently cleaned .... ", self]; + } + else if( ! _callbackOperation ){ + [NSException raise:NSInvalidArgumentException format:@" Warning :: Master Pool (%p) Warning (async) :: NO OPERATION :: was sudently cleaned .... ", self]; + } + else { + + void (^callback_master_failed)(void *,NSError* error ) = (__bridge void (^ _Nullable)( void *,NSError* ))( _callbackOperation ); + + // callback_master_failed(usedPassword ? YES : NO,nil); + [self _reconnectWithHandler:callback_master_failed]; + + if(!_connection ) + { + [NSException raise:NSInvalidArgumentException format:@" Warning :: Master Pool (%p) Warning (async) :: UNKNOW ERROR :: was sudently cleaned .... ", self]; + }else{ + NSLog(@" Warning :: Master Pool (%p) Warning (async) :: UNKNOW ERROR :: But Reconnected :: was sudently cleaned .... ", self); + } + } + + dispatch_semaphore_signal(master_semaphore_lock); + // [((PGConnectionOperation*)[self masterPoolOperation]) finish]; + } @catch (NSException *exception) { + NSLog(@"**************************** \n ERROR :: %@ :: \n **************************** \n [ %@ ] \n **************************** \n ", NSStringFromSelector(_cmd), exception); + // dispatch_semaphore_t ss = [((PGConnectionOperation*)[self masterPoolOperation]) semaphore]; // dispatch_semaphore_create(0); + dispatch_semaphore_signal(master_semaphore_lock); + } @finally { + + } + + } + + + ); + + // dispatch_semaphore_wait(ss,DISPATCH_TIME_FOREVER); + // [self wait_semaphore_read:ss]; + [self wait_semaphore_read: [((PGConnectionOperation*)[self masterPoolOperation]) semaphore] withQueue:queue_inRun]; + // [((PGConnectionOperation*)[self masterPoolOperation]) validate]; + } + // dispatch_destroy(queue); + } else if(needsPassword) { + // error callback - connection not made, needs password + callback(NO,[self raiseError:nil code:PGClientErrorNeedsPassword]); + } else if(usedPassword) { + // error callback - connection not made, password was invalid + callback(YES,[self raiseError:nil code:PGClientErrorInvalidPassword]); + } else { + // error callback - connection not made, some other kind of rejection + callback(YES,[self raiseError:nil code:PGClientErrorRejected]); + } + +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"%@ (%p) :: END :: - Read::END - %@ :: free (callback %p)", NSStringFromClass([self class]), self, NSStringFromSelector(_cmd), callback); +#endif + // :: TODO :: + //:: _callback = nil; + // + // [((PGConnectionOperation*)[self currentPoolOperation]) invalidate]; + // [((PGConnectionOperation*)[self masterPoolOperation]) invalidate]; + // [self pushPoolOperation]; + + + } @catch (NSException *exception) { + NSLog(@"**************************** \n ERROR :: %@ :: \n **************************** \n [ %@ ] \n **************************** \n ", NSStringFromSelector(_cmd), exception); + dispatch_semaphore_t ss = [((PGConnectionOperation*)[self masterPoolOperation]) semaphore]; // dispatch_semaphore_create(0); + dispatch_semaphore_signal(ss); + } @finally { + + } } -(void)_socketCallbackResetEndedWithStatus:(PostgresPollingStatusType)pqstatus { - NSParameterAssert(_callback); - void (^callback)(NSError* error) = (__bridge void (^)(NSError* ))(_callback); - if(pqstatus==PGRES_POLLING_OK) { - callback(nil); - } else { - callback([self raiseError:nil code:PGClientErrorRejected]); - } - _callback = nil; - [self setState:PGConnectionStateNone]; - [self _updateStatus]; // this also calls disconnect when rejected + NSParameterAssert([[self currentPoolOperation] valid]); + //:: void (^callback)(NSError* error) = (__bridge void (^)(NSError* ))(_callback); + void (^callback)(NSError* error) = (__bridge void (^)(NSError* ))( [((PGConnectionOperation*)[self currentPoolOperation]) getCallback] ); + if(pqstatus==PGRES_POLLING_OK) { + callback(nil); + } else { + callback([self raiseError:nil code:PGClientErrorRejected]); + } + //:: _callback = nil; + [((PGConnectionOperation*)[self currentPoolOperation]) invalidate]; + [self setState:PGConnectionStateNone]; + [self _updateStatus]; // this also calls disconnect when rejected } /** @@ -152,26 +310,39 @@ -(void)_socketCallbackResetEndedWithStatus:(PostgresPollingStatusType)pqstatus { * run. */ -(void)_socketCallbackConnect { - // ignore this call if either connection or callback are nil - if(_connection==nil || _callback==nil) { - return; - } - - PostgresPollingStatusType pqstatus = PQconnectPoll(_connection); - switch(pqstatus) { - case PGRES_POLLING_READING: - case PGRES_POLLING_WRITING: - // still connecting - ask to call poll again in runloop - [self performSelector:@selector(_socketCallbackConnect) withObject:nil afterDelay:0.1]; - break; - case PGRES_POLLING_OK: - case PGRES_POLLING_FAILED: - // finished connecting - [self _socketCallbackConnectEndedWithStatus:pqstatus]; - break; - default: - break; - } + // ignore this call if either connection or callback are nil + + PGConnectionOperation* currentpoolOpe = [self currentPoolOperation]; + if(_connection==nil + || ![[self currentPoolOperation] valid] + || ( [currentpoolOpe poolIdentifier]) !=0 + ) { + // || (_callback==nil) // && _callbackOperation == nil) + return; + } + + PostgresPollingStatusType pqstatus = PQconnectPoll(_connection); + switch(pqstatus) { + case PGRES_POLLING_READING: + case PGRES_POLLING_WRITING: + // still connecting - ask to call poll again in runloop + [self performSelector:@selector(_socketCallbackConnect) withObject:nil afterDelay:0.1]; + break; + case PGRES_POLLING_OK: + case PGRES_POLLING_FAILED: + if(( [currentpoolOpe poolIdentifier]) ==0 && [currentpoolOpe valid] ) + { + // finished connecting + // [self performSelectorInBackground:@selector(_socketCallbackConnectEndedWithStatus:) withObject:nil]; + [self performSelector:@selector(_socketCallbackConnectEndedWithStatus:) withObject:nil afterDelay:0.1]; + + // [self performSelector:@selector(_socketCallbackConnectEndedWithStatus:) withObject:nil]; + } + // [self _socketCallbackConnectEndedWithStatus:pqstatus]; + break; + default: + break; + } } /** @@ -179,23 +350,23 @@ -(void)_socketCallbackConnect { * be merged with that one. */ -(void)_socketCallbackReset { - NSParameterAssert(_connection); - - PostgresPollingStatusType pqstatus = PQresetPoll(_connection); - switch(pqstatus) { - case PGRES_POLLING_READING: - case PGRES_POLLING_WRITING: - // still connecting - call poll again - PQresetPoll(_connection); - break; - case PGRES_POLLING_OK: - case PGRES_POLLING_FAILED: - // finished connecting - [self _socketCallbackResetEndedWithStatus:pqstatus]; - break; - default: - break; - } + NSParameterAssert(_connection); + + PostgresPollingStatusType pqstatus = PQresetPoll(_connection); + switch(pqstatus) { + case PGRES_POLLING_READING: + case PGRES_POLLING_WRITING: + // still connecting - call poll again + PQresetPoll(_connection); + break; + case PGRES_POLLING_OK: + case PGRES_POLLING_FAILED: + // finished connecting + [self _socketCallbackResetEndedWithStatus:pqstatus]; + break; + default: + break; + } } /** @@ -203,54 +374,129 @@ -(void)_socketCallbackReset { * then any results from the server */ -(void)_socketCallbackQueryRead { - NSParameterAssert(_connection); - - PQconsumeInput(_connection); - - /* it seems that we don't really need to check for busy and it seems to - * create some issues, so ignore for now - // check for busy, return if more to do - if(PQisBusy(_connection)) { - return; - } - */ - - // consume results - PGresult* result = nil; - while(1) { - result = PQgetResult(_connection); - if(result==nil) { - break; - } - NSError* error = nil; - PGResult* r = nil; - // check for connection errors - if(PQresultStatus(result)==PGRES_EMPTY_QUERY) { - // callback empty query - error = [self raiseError:nil code:PGClientErrorQuery reason:@"Empty query"]; - PQclear(result); - } else if(PQresultStatus(result)==PGRES_BAD_RESPONSE || PQresultStatus(result)==PGRES_FATAL_ERROR) { - error = [self raiseError:nil code:PGClientErrorExecute reason:[NSString stringWithUTF8String:PQresultErrorMessage(result)]]; - PQclear(result); - } else { - // TODO: allocate a different kind of class - r = [[PGResult alloc] initWithResult:result format:[self tupleFormat]]; - } - if(r || error) { - NSParameterAssert(_callback); - void (^callback)(PGResult* result,NSError* error) = (__bridge void (^)(PGResult* ,NSError* ))(_callback); - // queue up callback on main thread - dispatch_async(dispatch_get_main_queue(),^{ - callback(r,error); - }); - } - } - - // all results consumed - update state - [self setState:PGConnectionStateNone]; - - _callback = nil; // release the callback - [self _updateStatus]; + NSParameterAssert(_connection); + + bool pgBusy = false; + + @synchronized (self) { + + + PQconsumeInput(_connection); + + /* it seems that we don't really need to check for busy and it seems to + * create some issues, so ignore for now */ + // check for busy, return if more to do + + pgBusy = PQisBusy(_connection); + } + + if(pgBusy) { + return; + } + + // + NSParameterAssert([[self currentPoolOperation] valid]); + + PGConnectionOperation* currentPoolOperation = [self currentPoolOperation]; + + //:: void (^callback)(PGResult* result,NSError* error) = (__bridge void (^)(PGResult* ,NSError* ))(_callback); + + void (^callback)(PGResult* result,NSError* error) = (__bridge void (^)(PGResult* ,NSError* ))( [((PGConnectionOperation*)[self currentPoolOperation]) getCallback] ); + +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@":::: PGConnectionStateQuery(%@::%p) - Read BEGIN - Result - :: callback \n_callback :: (%@)\n ********************************* ",NSStringFromClass([self class]), self , callback ); +#endif + + + // consume results + PGresult* result = nil; + while(1) { + result = PQgetResult(_connection); + if(result==nil) { +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read - Result nil - End"); +#endif + break; + } + NSError* error = nil; + PGResult* r = nil; + + + // check for connection errors + if(PQresultStatus(result)==PGRES_EMPTY_QUERY) { +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read - Result - Empty Query"); +#endif + // callback empty query + error = [self raiseError:nil code:PGClientErrorQuery reason:@"Empty query"]; + PQclear(result); + } else if(PQresultStatus(result)==PGRES_BAD_RESPONSE || PQresultStatus(result)==PGRES_FATAL_ERROR) { +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read - Result - Client Error (%s)", PQresultErrorMessage(result)); +#endif + error = [self raiseError:nil code:PGClientErrorExecute reason:[NSString stringWithUTF8String:PQresultErrorMessage(result)]]; + PQclear(result); + } else { +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read - Result - READ Query RESULT (%@)",[currentPoolOperation queryString] ); +#endif + // TODO: allocate a different kind of class + r = [[PGResult alloc] initWithResult:result format:[self tupleFormat]]; +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read - Result - READ END (%s)", PQresultErrorMessage(result)); +#endif +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read - Result - Done :: (%@)", r ); +#endif + } + if(r || error) { + +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery(%@::%p) - Read - Result - Done :: callback \n ********************************* \nresult :: (%@)\n ********************************* \nerror :: (%@) \n_callback :: (%@)\n ********************************* ",NSStringFromClass([self class]), self, r, error, callback ); +#endif + // queue up callback on nearest thread + + dispatch_queue_t qu_inRun = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); + + dispatch_async( qu_inRun ,^{ +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read - callback %p ",callback); +#endif + @try + { + id curope = [self currentPoolOperation]; + id queryResults = [ curope setResults: r ]; + + callback(r,error); + }@catch (NSException *exception) { + NSLog(@" %@ :: read callback (async) :: exeception .... %@",NSStringFromSelector(_cmd),exception); + } + @finally { + + } + [((PGConnectionOperation*)[self currentPoolOperation]) invalidate]; + // [((PGConnectionOperation*)[self currentPoolOperation]) finish]; + [((PGConnectionOperation*)[self masterPoolOperation]) validate]; + }); + + break; + } + } +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read - Result - Clear " ); +#endif + // all results consumed - update state + [self setState:PGConnectionStateNone]; + + //:: _callback = nil; // release the callback + [((PGConnectionOperation*)[self masterPoolOperation]) invalidate]; + // [((PGConnectionOperation*)[self currentPoolOperation]) invalidate]; + + [self _updateStatus]; +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read END - Result - Clear::End " ); +#endif + _stateOperation = PGOperationStateNone; } /** @@ -258,16 +504,17 @@ -(void)_socketCallbackQueryRead { * flush the connection and consume any results which are being processed. */ -(void)_socketCallbackQueryWrite { - NSParameterAssert(_connection); - // flush - int returnCode = PQflush(_connection); - if(returnCode==-1) { - // callback with error - NSParameterAssert(_callback); - void (^callback)(PGResult* result,NSError* error) = (__bridge void (^)(PGResult* ,NSError* ))(_callback); - NSError* error = [self raiseError:nil code:PGClientErrorState reason:@"Data flush failed during query"]; - callback(nil,error); - } + NSParameterAssert(_connection); + // flush + int returnCode = PQflush(_connection); + if(returnCode==-1) { + // callback with error + NSParameterAssert([[self currentPoolOperation] valid]); + //:: void (^callback)(PGResult* result,NSError* error) = (__bridge void (^)(PGResult* ,NSError* ))(_callback); + void (^callback)(PGResult* result,NSError* error) = (__bridge void (^)(PGResult* ,NSError* ))( [((PGConnectionOperation*)[self currentPoolOperation]) getCallback] ); + NSError* error = [self raiseError:nil code:PGClientErrorState reason:@"Data flush failed during query"]; + callback(nil,error); + } } /** @@ -276,65 +523,77 @@ -(void)_socketCallbackQueryWrite { * or notification socket callback */ -(void)_socketCallback:(CFSocketCallBackType)callBackType { - -#if defined DEBUG && defined DEBUG2 - switch(callBackType) { - case kCFSocketReadCallBack: - NSLog(@"kCFSocketReadCallBack"); - break; - case kCFSocketAcceptCallBack: - NSLog(@"kCFSocketAcceptCallBack"); - break; - case kCFSocketDataCallBack: - NSLog(@"kCFSocketDataCallBack"); - break; - case kCFSocketConnectCallBack: - NSLog(@"kCFSocketConnectCallBack"); - break; - case kCFSocketWriteCallBack: - NSLog(@"kCFSocketWriteCallBack"); - break; - default: - NSLog(@"CFSocketCallBackType OTHER"); - break; - } + @try{ +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + switch(callBackType) { + case kCFSocketReadCallBack: + NSLog(@"kCFSocketReadCallBack"); + break; + case kCFSocketAcceptCallBack: + NSLog(@"kCFSocketAcceptCallBack"); + break; + case kCFSocketDataCallBack: + NSLog(@"kCFSocketDataCallBack"); + break; + case kCFSocketConnectCallBack: + NSLog(@"kCFSocketConnectCallBack"); + break; + case kCFSocketWriteCallBack: + NSLog(@"kCFSocketWriteCallBack"); + break; + default: + NSLog(@"CFSocketCallBackType OTHER"); + break; + } +#endif + PGConnectionState state_on = [self state]; + switch(state_on) { + case PGConnectionStateConnect: +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateConnect"); #endif - switch([self state]) { - case PGConnectionStateConnect: -#if defined DEBUG && defined DEBUG2 - NSLog(@"PGConnectionStateConnect"); + [self _socketCallbackConnect]; + break; + case PGConnectionStateReset: +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateReset"); #endif - [self _socketCallbackConnect]; - break; - case PGConnectionStateReset: -#if defined DEBUG && defined DEBUG2 - NSLog(@"PGConnectionStateReset"); + [self _socketCallbackReset]; + break; + case PGConnectionStateQuery: + if(callBackType==kCFSocketReadCallBack) { +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read - _socketCallback :: _socketCallbackQueryRead :: callback (%p)",[[self currentPoolOperation] getCallback]); #endif - [self _socketCallbackReset]; - break; - case PGConnectionStateQuery: - if(callBackType==kCFSocketReadCallBack) { -#if defined DEBUG && defined DEBUG2 - NSLog(@"PGConnectionStateQuery - Read"); + [self _socketCallbackQueryRead]; +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Read - _socketCallback :: _socketCallbackNotification :: callback (%p)", [[self currentPoolOperation] getCallback]); #endif - [self _socketCallbackQueryRead]; - [self _socketCallbackNotification]; - } else if(callBackType==kCFSocketWriteCallBack) { -#if defined DEBUG && defined DEBUG2 - NSLog(@"PGConnectionStateQuery - Write"); + [self _socketCallbackNotification]; + + } else if(callBackType==kCFSocketWriteCallBack) { +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - Write"); #endif - [self _socketCallbackQueryWrite]; - } - break; - default: -#if defined DEBUG && defined DEBUG2 - NSLog(@"PGConnectionStateOther"); + [self _socketCallbackQueryWrite]; + } + break; + default: +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateOther"); #endif - [self _socketCallbackNotification]; - break; - } - - [self _updateStatus]; + [self _socketCallbackNotification]; + break; + } + + [self _updateStatus]; + } + @catch (NSException *exception) { + NSLog(@" %@ exeception .... %@",NSStringFromSelector(_cmd),exception); + } + @finally { + + } } @end diff --git a/src/Frameworks/PGClientKit/PGConnection+Connect.m b/src/Frameworks/PGClientKit/PGConnection+Connect.m index b6751ab..f669668 100644 --- a/src/Frameworks/PGClientKit/PGConnection+Connect.m +++ b/src/Frameworks/PGClientKit/PGConnection+Connect.m @@ -90,62 +90,179 @@ -(NSDictionary* )_connectionParametersForURL:(NSURL* )theURL { //////////////////////////////////////////////////////////////////////////////// #pragma mark public methods - connections //////////////////////////////////////////////////////////////////////////////// +-(void) _reconnectWithHandler: ( void(^ _Nullable )(void * pm,NSError* error)) callback +{ +// if(_connection && _socket) +// { +// [self disconnect]; +// } + + if(_connection && callback == nil){ + PQfinish(_connection); + _connection = nil; + } + if(_socket && callback == nil){ + [self _socketDisconnect]; + _socket = nil; + } + + if(!_connection && !_socket) + { + NSLog(@" %@ :: %@",NSStringFromSelector(_cmd), callback); + // extract connection parameters + NSDictionary* parameters = [self _connectionParametersForURL: _connectedUrl]; + if(parameters==nil) { + if(callback != nil) callback(NO,[self raiseError:nil code:PGClientErrorParameters]); + return; + } + + // update the status as necessary + [self _updateStatus]; + + // create parameter pairs + PGKVPairs* pairs = makeKVPairs(parameters); + if(pairs==nil) { + if(callback != nil) callback(NO,[self raiseError:nil code:PGClientErrorParameters]); + return; + } + + // create connection + _connection = PQconnectStartParams(pairs->keywords,pairs->values,0); + freeKVPairs(pairs); + if(_connection==nil) { + if(callback != nil) callback(NO,[self raiseError:nil code:PGClientErrorParameters]); + return; + } + + _connectionClosed = NO; + + // check for initial bad connection status + if(PQstatus(_connection)==CONNECTION_BAD) { + PQfinish(_connection); + _connection = nil; + [self _updateStatus]; + if(callback != nil) callback(NO,[self raiseError:nil code:PGClientErrorParameters]); + return; + } + // not quite good for cascaded Operation + //:: _callback = (__bridge_retained void* )[callback copy]; + // So we do good things to deal with cascaded Operation + if(callback != nil){ + if(CFArrayGetCount(_callbackOperationPool)) + id mp = [[self masterPoolOperation] class]; + + [self addOperation:self withCallBackWhenDone: (__bridge_retained void* )callback withCallBackWhenError: (__bridge_retained void* )callback ]; + }else{ + if( ! CFArrayGetCount(_callbackOperationPool)) + [self addOperation:self withCallBackWhenDone: (__bridge_retained void* )callback withCallBackWhenError: (__bridge_retained void* )callback ]; + [self _socketConnect:PGConnectionStateConnect]; + } + + NSLog(@" %@ :: connection Initialized (%p :: connection : %p :: socket : %p) ... ",NSStringFromSelector(_cmd), self, _connection, _socket); --(void)connectWithURL:(NSURL* )url whenDone:(void(^)(BOOL usedPassword,NSError* error)) callback { - NSParameterAssert(url); - NSParameterAssert(callback); + }else{ + NSLog(@" %@ :: connection seems good ... ",NSStringFromSelector(_cmd)); + } +} +-(void)connectWithURL:(NSURL* )url whenDone:(void(^)(BOOL usedPassword,NSError* error)) callback { + NSParameterAssert(url!=nil); + NSParameterAssert(callback!=nil); + @try { + // check for bad initial state - if(_connection != nil || [self state] != PGConnectionStateNone) { + if(_connection != nil ) { callback(NO,[self raiseError:nil code:PGClientErrorState]); return; } - + PGConnectionState curstate = [self state]; + if( curstate != PGConnectionStateNone) + { + NSLog(@" :: Warning :: %@ :: %@ :: \n :: Connection previously initialized ::", NSStringFromSelector(_cmd), self); + ;; + } + // check other internal variable consistency NSParameterAssert(_connection==nil); NSParameterAssert(_socket==nil); NSParameterAssert(_runloopsource==nil); + + _connectedUrl = url; + +#if ( defined(__IPHONE_10_3) && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3 ) || ( defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_12 ) + [NSThread detachNewThreadWithBlock:^{ +#else - // extract connection parameters - NSDictionary* parameters = [self _connectionParametersForURL:url]; - if(parameters==nil) { - callback(NO,[self raiseError:nil code:PGClientErrorParameters]); - return; - } +// dispatch_async(dispatch_get_main_queue(),^{ +#endif - // update the status as necessary - [self _updateStatus]; - - // create parameter pairs - PGKVPairs* pairs = makeKVPairs(parameters); - if(pairs==nil) { - callback(NO,[self raiseError:nil code:PGClientErrorParameters]); - return; - } + // set callback + NSParameterAssert(callback!=nil); + +// void (^callbackRecall)(bool result,NSError* error) = (__bridge void (^)(bool ,NSError* ))( callback); + void (^ _Nullable callbackRecall)(void * pm,NSError* error) = ( void (^ _Nullable )(void* ,NSError* ))( callback ); + + [self _reconnectWithHandler: callbackRecall]; + + + + #if ( defined(__IPHONE_10_3) && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3 ) || ( defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_12 ) + } ]; +#else - // create connection - _connection = PQconnectStartParams(pairs->keywords,pairs->values,0); - freeKVPairs(pairs); - if(_connection==nil) { - callback(NO,[self raiseError:nil code:PGClientErrorParameters]); - return; - } - - // check for initial bad connection status - if(PQstatus(_connection)==CONNECTION_BAD) { - PQfinish(_connection); - _connection = nil; - [self _updateStatus]; - callback(NO,[self raiseError:nil code:PGClientErrorParameters]); - return; - } +// } ); +#endif + // add socket to run loop +#if ( defined(__IPHONE_10_3) && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3 ) || ( defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_12 ) + [NSThread detachNewThreadWithBlock:^ +#else +// dispatch_get_current_queue +// dispatch_async(dispatch_get_current_queue(),^ +#endif + { +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" ------- %@ :: %@ :::: Connection Started ....", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); +#endif + + // start queued request + [self _socketConnect:PGConnectionStateConnect]; + +// if( _socket == nil) +// { +// NSLog(@" ERROR :: Premature Closing of socket .... (%@) ", self); +// +// if(_connection) +// PQfinish(_connection); +// +// _connection = nil; +// +// callback(NO,[self raiseError:nil code:PGClientErrorUnknown]); +// +// } +// CFRunLoopRun(); +// [self performSelector:@selector(_waitingPoolOperationForResult) withObject:self ]; +// [self performSelector:@selector(_waitingPoolOperationForResultMaster) withObject:self ]; - // set callback - NSParameterAssert(_callback==nil); - _callback = (__bridge_retained void* )[callback copy]; +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" ------- %@ :: %@ :::: Connection STOPPed ....", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); +#endif + + } +#if ( defined(__IPHONE_10_3) && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3 ) || ( defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_12 ) + ] +#else + +// ) +#endif + ; + + } @catch (NSException *exception) { + NSLog(@"**************************** \n ERROR connection init :: %@ :: \n **************************** \n [ %@ ] \n **************************** \n ", NSStringFromSelector(_cmd), exception); + return; + } @finally { + + } - // add socket to run loop - [self _socketConnect:PGConnectionStateConnect]; } -(BOOL)connectWithURL:(NSURL* )url usedPassword:(BOOL* )usedPassword error:(NSError** )error { diff --git a/src/Frameworks/PGClientKit/PGConnection+Disconnect.m b/src/Frameworks/PGClientKit/PGConnection+Disconnect.m index f56749d..091d484 100644 --- a/src/Frameworks/PGClientKit/PGConnection+Disconnect.m +++ b/src/Frameworks/PGClientKit/PGConnection+Disconnect.m @@ -18,14 +18,21 @@ @implementation PGConnection (Disconnect) -(void)disconnect { - [self _cancelDestroy]; - [self _socketDisconnect]; - if(_connection) { - PQfinish(_connection); - _connection = nil; - _parameters = nil; - } - [self _updateStatus]; + @synchronized (self) { + [self _cancelDestroy]; + [self _socketDisconnect]; + if(_connection != NULL) { + + + PQfinish(_connection); + _connection = nil; + _parameters = nil; + _connectionClosed = YES; + + } + NSLog(@" %@ (%p) :: %@ :: Close connection pool (%ld) :: (%@) .... ", NSStringFromClass([self class]), self, NSStringFromSelector(_cmd), CFArrayGetCount(_callbackOperationPool), _connectedUrl); + } + [self _updateStatus]; } @end diff --git a/src/Frameworks/PGClientKit/PGConnection+Errors.m b/src/Frameworks/PGClientKit/PGConnection+Errors.m index a3eadd9..0e1feeb 100644 --- a/src/Frameworks/PGClientKit/PGConnection+Errors.m +++ b/src/Frameworks/PGClientKit/PGConnection+Errors.m @@ -69,8 +69,26 @@ -(void)_raiseError:(NSError* )error { NSParameterAssert(error); // perform selector if([[self delegate] respondsToSelector:@selector(connection:error:)]) { - [[self delegate] connection:self error:error]; - } +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - _raiseError :: send to delegate connection:error:" ); +#endif +// [[self delegate] connection:self error:error]; +// @{@"connection":self, @"error":error} + +// dispatch_queue_t qu_inRun = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); +// dispatch_barrier_sync(qu_inRun, ^ { + [[self delegate] connection:self error:error]; +// [NSThread detachNewThreadSelector:@selector(connection:error:) toTarget:[self delegate] withObject:@{@"connection":self, @"error":error} ]; + #if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - _raiseError :: send to delegate connection:error: END" ); +#endif +//} ); + + }else{ +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - _raiseError :: no delegate respondsto connection:error:" ); +#endif + } } -(NSError* )raiseError:(NSError** )error code:(PGClientErrorDomainCode)code reason:(NSString* )format,... { @@ -89,8 +107,22 @@ -(NSError* )raiseError:(NSError** )error code:(PGClientErrorDomainCode)code reas } // raise the error with the delegate if([self delegate]) { - [self performSelectorOnMainThread:@selector(_raiseError:) withObject:theError waitUntilDone:YES]; - } +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - _raiseError :: performSelectorOnMainThread :: (%@)", theError ); +#endif + + +// [self performSelectorOnMainThread:@selector(_raiseError:) withObject:theError waitUntilDone:YES]; + + [self _raiseError:theError]; + }else{ +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - _raiseError:code: :: no delegate avail" ); +#endif + } +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnectionStateQuery - _raiseError :: return " ); +#endif return theError; } diff --git a/src/Frameworks/PGClientKit/PGConnection+Execute.m b/src/Frameworks/PGClientKit/PGConnection+Execute.m index 33f6a34..9bf92fc 100644 --- a/src/Frameworks/PGClientKit/PGConnection+Execute.m +++ b/src/Frameworks/PGClientKit/PGConnection+Execute.m @@ -12,6 +12,16 @@ // License for the specific language governing permissions and limitations // under the License. +// ************************************* +// +// Copyright 2017 - ?? Sebastien Cotillard - Genose.org +// 07/2017 Sebastien Cotillard +// https://github.com/genose +// +// ************************************* +// ADDING Pool concurrent operation +// ************************************* + #import #import @@ -21,174 +31,527 @@ @implementation PGConnection (Execute) #pragma mark private methods - statement execution //////////////////////////////////////////////////////////////////////////////// + + -(void)_execute:(NSString* )query values:(NSArray* )values whenDone:(void(^)(PGResult* result,NSError* error)) callback { - NSParameterAssert(query && [query isKindOfClass:[NSString class]]); - if(_connection==nil || [self state] != PGConnectionStateNone) { - callback(nil,[self raiseError:nil code:PGClientErrorState]); - return; - } - - // create parameters object - PGClientParams* params = _paramAllocForValues(values); - if(params==nil) { - callback(nil,[self raiseError:nil code:PGClientErrorParameters]); - return; - } - // convert parameters - for(NSUInteger i = 0; i < [values count]; i++) { - id obj = [values objectAtIndex:i]; - if([obj isKindOfClass:[NSNull class]]) { - _paramSetNull(params,i); - continue; - } - if([obj isKindOfClass:[NSString class]]) { - NSData* data = [(NSString* )obj dataUsingEncoding:NSUTF8StringEncoding]; - _paramSetBinary(params,i,data,(Oid)25); - continue; - } - // TODO - other kinds of parameters - NSLog(@"TODO: Turn %@ into arg",[obj class]); - _paramSetNull(params,i); - } - // check number of parameters - if(params->size > INT_MAX) { - _paramFree(params); - callback(nil,[self raiseError:nil code:PGClientErrorParameters]); - return; - } - - // call the delegate, determine the class to use for the resultset - NSString* className = nil; - if([[self delegate] respondsToSelector:@selector(connection:willExecute:)]) { - className = [[self delegate] connection:self willExecute:query]; - } - - // execute the command, free parameters - int resultFormat = ([self tupleFormat]==PGClientTupleFormatBinary) ? 1 : 0; - int returnCode = PQsendQueryParams(_connection,[query UTF8String],(int)params->size,params->types,(const char** )params->values,params->lengths,params->formats,resultFormat); - _paramFree(params); - if(!returnCode) { - callback(nil,[self raiseError:nil code:PGClientErrorExecute reason:[NSString stringWithUTF8String:PQerrorMessage(_connection)]]); - return; - } - - // set state, update status - [self setState:PGConnectionStateQuery]; - - [self _updateStatus]; - NSParameterAssert(_callback==nil); - _callback = (__bridge_retained void* )[callback copy]; + + + NSParameterAssert(query && [query isKindOfClass:[NSString class]]); + + if(_connection==nil ) { + callback(nil,[self raiseError:nil code:PGClientErrorState]); + return; + } + + PGConnectionState curstate = [self state]; + if( curstate != PGConnectionStateNone) + { + NSLog(@" :: Warning :: %@ :: %@ :: \n :: Wrong State :: %u ", NSStringFromSelector(_cmd), self, curstate); + ;; + } + + + // create parameters object + PGClientParams* params = _paramAllocForValues(values); + if(params==nil) { + callback(nil,[self raiseError:nil code:PGClientErrorParameters]); + return; + } + // convert parameters + for(NSUInteger i = 0; i < [values count]; i++) { + id obj = [values objectAtIndex:i]; + if([obj isKindOfClass:[NSNull class]]) { + _paramSetNull(params,i); + continue; + } + if([obj isKindOfClass:[NSString class]]) { + NSData* data = [(NSString* )obj dataUsingEncoding:NSUTF8StringEncoding]; + _paramSetBinary(params,i,data,(Oid)25); + continue; + } + // TODO - other kinds of parameters + NSLog(@"TODO: Turn %@ into arg",[obj class]); + _paramSetNull(params,i); + } + + + // check number of parameters + if(params->size > INT_MAX) { + _paramFree(params); + callback(nil,[self raiseError:nil code:PGClientErrorParameters]); + return; + } + + // call the delegate, determine the class to use for the resultset + NSString* className = nil; + if([[self delegate] respondsToSelector:@selector(connection:willExecute:)]) { + className = [[self delegate] connection:self willExecute:query]; + } + + // execute the command, free parameters + int resultFormat = 0; + int returnCode = 0; + @synchronized(self) + { + // set state, update status + [self setState:PGConnectionStateQuery]; + + [self _updateStatus]; + + // execute the command, free parameters + resultFormat = ([self tupleFormat]==PGClientTupleFormatBinary) ? 1 : 0; + returnCode = PQsendQueryParams(_connection,[query UTF8String],(int)params->size,params->types,(const char** )params->values,params->lengths,params->formats,resultFormat); + + } + + _paramFree(params); + + if(!returnCode) { +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"%@ :: %@ :: - execute - ERROR :: callback %p :: While EXECUTE (%@)", NSStringFromSelector(_cmd),NSStringFromClass([self class]), callback, query); +#endif + callback(nil,[self raiseError:nil code:PGClientErrorExecute reason:[NSString stringWithUTF8String:PQerrorMessage(_connection)]]); + return; + }else{ +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"%@ :: %@ :: - execute - PASSED :: callback %p :: While EXECUTE (%@)", NSStringFromSelector(_cmd),NSStringFromClass([self class]), callback, query); +#endif + } + + + NSParameterAssert(callback!=nil); + + // _callbackOperation = (__bridge_retained void* )[callback copy]; + + // if([self currentPoolOperation] != nil) [[self currentPoolOperation] invalidate]; + + [self addOperation:query withCallBackWhenDone: (__bridge_retained void* )callback withCallBackWhenError: (__bridge_retained void* )callback ]; + + +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"%@ :: %@ :: - execute - RETURN :: callback %p :: %p ", NSStringFromSelector(_cmd),NSStringFromClass([self class]), callback, _callbackOperation); +#endif + + // NSParameterAssert(_callbackOperation!=nil); + + // [NSThread sleepForTimeInterval: .2]; } //////////////////////////////////////////////////////////////////////////////// #pragma mark public methods - execution //////////////////////////////////////////////////////////////////////////////// --(void)execute:(id)query whenDone:(void(^)(PGResult* result,NSError* error)) callback { - NSParameterAssert([query isKindOfClass:[NSString class]] || [query isKindOfClass:[PGQuery class]]); - NSParameterAssert(callback); - NSString* query2 = nil; - NSError* error = nil; - if([query isKindOfClass:[NSString class]]) { - query2 = query; - } else { - query2 = [(PGQuery* )query quoteForConnection:self error:&error]; - } - if(error) { - callback(nil,error); - } else if(query2==nil) { - callback(nil,[self raiseError:nil code:PGClientErrorExecute reason:@"Query is nil"]); - } else { - [self _execute:query2 values:nil whenDone:callback]; - } +-(id)execute:(id)query whenDone:(void(^)(PGResult* result,NSError* error)) callback { + + NSParameterAssert([query isKindOfClass:[NSString class]] || [query isKindOfClass:[PGQuery class]]); + NSParameterAssert(callback); + + if( ! _connection ) + { + + +// [self disconnect]; + + void (^ _Nullable callbackRecall)(void * pm,NSError* error) = ( void (^ _Nullable )(void* ,NSError* ))( callback ); + +// [self setState:PGConnectionStateNone]; + +// [self _reconnectWithHandler: nil]; + if( ! _connection || !_socket) + { + callback(nil,[self raiseError:nil code:PGClientErrorUnknown]); +// [[self masterPoolOperation] invalidate]; +// [[self masterPoolOperation] finish]; + } + + + + + return nil; + } + + _stateOperation = PGOperationStatePrepare; + + // mach_port_t machTID = pthread_mach_thread_np(pthread_self()); + NSString * queued_name_STR = [NSString stringWithFormat:@"%s_%ld :: %@", "query_operation_dispacthed_threads", (long)[self connectionPoolOperationCount], [NSThread currentThread] ]; + const char * queued_name = [queued_name_STR UTF8String]; +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" //// -_-_-_-_ Query Dispatch START -_-_-_-_- :: %@ ", queued_name_STR); +#endif + dispatch_queue_t queue_inRun = dispatch_queue_create(queued_name, DISPATCH_QUEUE_CONCURRENT); + + NSString* query2 = nil; + NSError* error = nil; + __block id queryResults = nil; + + if([query isKindOfClass:[NSString class]]) { + query2 = query; + } else { + query2 = [(PGQuery* )query quoteForConnection:self error:&error]; + } + if(error) { +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"%@ :: %@ - query ERROR - callback %p :: \n query :: %@ :::::",NSStringFromClass([self class]), NSStringFromSelector(_cmd), callback, query2); +#endif + callback(nil,error); + } else if(query2==nil) { + callback(nil,[self raiseError:nil code:PGClientErrorExecute reason:@"Query is nil"]); + } else { +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"%@ :: %@ - query - callback %p :: \n query :: %@ :::::",NSStringFromClass([self class]), NSStringFromSelector(_cmd), callback, query2); +#endif + void (^callback_recall)(PGResult* result,NSError* error) = ^(PGResult* result_recall ,NSError* error_recall) + { +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" .... semaphore callback..... %@", [[self currentPoolOperation] semaphore]); +#endif +// id curope = [self currentPoolOperation]; +// queryResults = [ curope setResults: result_recall ]; +// [result_recall fetchRowAsDictionary] + callback(result_recall , error_recall); + +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" .... semaphore pass ..... "); + // dispatch_semaphore_signal( [[self currentPoolOperation] semaphore] ); + NSLog(@" .... semaphore signal end ..... %@", [[self currentPoolOperation] semaphore]); +#endif + + }; + // dispatch_semaphore_signal(semaphore_query_send); + [self _execute:query2 values:nil whenDone: callback_recall]; + + } + + + _stateOperation = PGOperationStateBusy; + // [NSThread sleepForTimeInterval:.2]; + dispatch_queue_t qu_inRun = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); + // NSLog(@"+++ Is main queue? : %d", qu_inRun == dispatch_get_main_queue()); + + id curope = [self currentPoolOperation]; + + dispatch_semaphore_t semaphore_query_send = [curope semaphore]; + [self wait_semaphore_read: semaphore_query_send withQueue:queue_inRun]; + +// NSLog(@" RESULTS ::\n Query : %@ \n:: Result : %@ ", query2, queryResults); + +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" -_-_-_-_ Query END -_-_-_-_- :: Query : %@ :: %@ ", query2, queued_name_STR); +#endif + _stateOperation = PGOperationStateNone; + return (queryResults)?[queryResults copy] : nil; } -(PGResult* )execute:(id)query error:(NSError** )error { - dispatch_semaphore_t s = dispatch_semaphore_create(0); - __block PGResult* result = nil; - [self execute:query whenDone:^(PGResult* r, NSError* e) { - if(error) { - (*error) = e; - } - result = r; - dispatch_semaphore_signal(s); - }]; - dispatch_semaphore_wait(s,DISPATCH_TIME_FOREVER); - return result; + dispatch_semaphore_t s = dispatch_semaphore_create(0); + __block PGResult* result = nil; + [self execute:query whenDone:^(PGResult* r, NSError* e) { + if(error) { + (*error) = e; + } + result = r; + dispatch_semaphore_signal(s); + }]; + dispatch_semaphore_wait(s,DISPATCH_TIME_FOREVER); + return result; } -(void)_queue:(PGTransaction* )transaction index:(NSUInteger)i lastResult:(PGResult* )result lastError:(NSError* )error whenQueryDone:(void(^)(PGResult* result,BOOL isLastQuery,NSError* error)) callback { - if(error) { - // rollback - if([transaction transactional]) { - NSString* rollbackTransaction = [(PGTransaction* )transaction quoteRollbackTransactionForConnection:self]; - NSParameterAssert(rollbackTransaction); - [self execute:rollbackTransaction whenDone:^(PGResult* result2,NSError* error2) { - callback(nil,YES,error); - }]; - } else { - callback(nil,YES,error); - } - } else if(i==[transaction count]) { - // commit - if([transaction transactional]) { - NSString* commitTransaction = [(PGTransaction* )transaction quoteCommitTransactionForConnection:self]; - NSParameterAssert(commitTransaction); - [self execute:commitTransaction whenDone:^(PGResult* result2, NSError* error2) { - callback(result,YES,error); - }]; - } else { - callback(result,YES,error); - } - } else { - // execute a single query - [self execute:[transaction queryAtIndex:i] whenDone:^(PGResult* result,NSError* error) { - if(i < [transaction count]) { - [self _queue:transaction index:(i+1) lastResult:result lastError:error whenQueryDone:callback]; - } - }]; - } + if(error) { + // rollback + if([transaction transactional]) { + NSString* rollbackTransaction = [(PGTransaction* )transaction quoteRollbackTransactionForConnection:self]; + NSParameterAssert(rollbackTransaction); + [self execute:rollbackTransaction whenDone:^(PGResult* result2,NSError* error2) { + callback(nil,YES,error); + }]; + } else { + callback(nil,YES,error); + } + } else if(i==[transaction count]) { + // commit + if([transaction transactional]) { + NSString* commitTransaction = [(PGTransaction* )transaction quoteCommitTransactionForConnection:self]; + NSParameterAssert(commitTransaction); + [self execute:commitTransaction whenDone:^(PGResult* result2, NSError* error2) { + callback(result,YES,error); + }]; + } else { + callback(result,YES,error); + } + } else { + // execute a single query + [self execute:[transaction queryAtIndex:i] whenDone:^(PGResult* result,NSError* error) { + if(i < [transaction count]) { + [self _queue:transaction index:(i+1) lastResult:result lastError:error whenQueryDone:callback]; + } + }]; + } } -(void)queue:(PGTransaction* )transaction whenQueryDone:(void(^)(PGResult* result,BOOL isLastQuery,NSError* error)) callback { - NSParameterAssert(transaction && [transaction isKindOfClass:[PGTransaction class]]); - - // where there are no transactions to execute, raise error immediately - if([transaction count]==0) { - callback(nil,YES,[self raiseError:nil code:PGClientErrorExecute reason:@"No transactions to execute"]); - return; - } - - // check for connection status - if(_connection==nil || [self state] != PGConnectionStateNone) { - callback(nil,YES,[self raiseError:nil code:PGClientErrorState]); - return; - } - - // check for transaction status - PGTransactionStatusType tstatus = PQtransactionStatus(_connection); - if([transaction transactional] && tstatus != PQTRANS_IDLE) { - callback(nil,YES,[self raiseError:nil code:PGClientErrorState reason:@"Already in a transaction"]); - return; - } - - if([transaction transactional]==NO) { - // queue zeroth query - [self _queue:transaction index:0 lastResult:nil lastError:nil whenQueryDone:callback]; - } else { - // queue up a start transaction, which triggers the first query - NSString* beginTransaction = [(PGTransaction* )transaction quoteBeginTransactionForConnection:self]; - NSParameterAssert(beginTransaction); - [self execute:beginTransaction whenDone:^(PGResult* result, NSError* error) { - // if the BEGIN transaction didn't work, then callback - if(error) { - callback(nil,YES,error); - } else { - // else queue up zeroth query - [self _queue:transaction index:0 lastResult:result lastError:error whenQueryDone:callback]; - } - }]; - } + NSParameterAssert(transaction && [transaction isKindOfClass:[PGTransaction class]]); + + // where there are no transactions to execute, raise error immediately + if([transaction count]==0) { + callback(nil,YES,[self raiseError:nil code:PGClientErrorExecute reason:@"No transactions to execute"]); + return; + } + + // check for connection status + if(_connection==nil || [self state] != PGConnectionStateNone) { + callback(nil,YES,[self raiseError:nil code:PGClientErrorState]); + return; + } + + // check for transaction status + PGTransactionStatusType transac_status = PQtransactionStatus(_connection); + if([transaction transactional] && transac_status != PQTRANS_IDLE) { + callback(nil,YES,[self raiseError:nil code:PGClientErrorState reason:@"Already in a transaction"]); + return; + } + + if([transaction transactional]==NO) { + // queue zeroth query + [self _queue:transaction index:0 lastResult:nil lastError:nil whenQueryDone:callback]; + } else { + // queue up a start transaction, which triggers the first query + NSString* beginTransaction = [(PGTransaction* )transaction quoteBeginTransactionForConnection:self]; + NSParameterAssert(beginTransaction); + [self execute:beginTransaction whenDone:^(PGResult* result, NSError* error) { + // if the BEGIN transaction didn't work, then callback + if(error) { + callback(nil,YES,error); + } else { + // else queue up zeroth query + [self _queue:transaction index:0 lastResult:result lastError:error whenQueryDone:callback]; + } + }]; + } +} + + + +#pragma mark -------- Sync Thread and Semaphores +-(void)wait_semaphore_read:(dispatch_semaphore_t) sem { + dispatch_queue_t qu_inRun = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); + + NSRunLoop *qq_loop = [NSRunLoop currentRunLoop]; + NSRunLoop *qq_loop_main = [NSRunLoop mainRunLoop]; + + + if( qq_loop != [NSRunLoop mainRunLoop]){ + + dispatch_barrier_sync(qu_inRun, ^{ +// @synchronized (self) { + [self wait_semaphore_read:sem withQueue:nil]; +// } + + }); + }else{ +// @synchronized (self) { + // dispatch_barrier_async(qu_inRun, ^{ + [self wait_semaphore_read:sem withQueue:nil]; +// } + // }); + } + +} +-(void)wait_semaphore_read:(dispatch_semaphore_t) sem withQueue:(dispatch_queue_t)qq_in { + + // mach_port_t machTID = pthread_mach_thread_np(pthread_self()); + + NSString *queued_name_STR = [NSString stringWithFormat:@"%@_%ld :: %s :: %@ ", NSStringFromSelector(_cmd), (long)[self connectionPoolOperationCount], + dispatch_queue_get_label(dispatch_get_main_queue()), + sem ]; + + // const char * queued_name = [queued_name_STR UTF8String]; +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" //// Start :: %@ ", queued_name_STR); +#endif + // NSLog(@" //// Start **** :: %s ", queued_name); + + long diispacthed = YES; + + bool PG_busy = YES; + + long timeoutThread = 120; // .05 * 2400 = 60s + + @try { + + while( + diispacthed + && _runloopsource + && _socket + && _connection ) + { + + timeoutThread -= 0.05; + +// PG_busy = PQisBusy(_connection); + diispacthed = dispatch_semaphore_wait(sem,DISPATCH_TIME_NOW); + + CFIndex indexInPool = CFArrayGetCount(_callbackOperationPool); + if(!indexInPool) + { + [NSException raise:NSInvalidArgumentException format:@" Warning :: Pool was sudently cleaned .... "]; + + break; + } + + [NSThread sleepForTimeInterval:0.05]; + + if( timeoutThread <= 0) + { + NSLog(@" //// STEP :: %@ :: Thread Time OUT %@ ",NSStringFromSelector(_cmd), queued_name_STR); + break; + } + + + if( nil == [[self currentPoolOperation] getCallback] || !diispacthed){ +// [NSThread sleepForTimeInterval:0.1]; + break; + } + + + + + if([[self currentPoolOperation] valid] ) + { +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" //// STEP :: %@ ", queued_name_STR); +#endif + [self performSelector:@selector(dispathCall) withObject:nil]; + }else{ + break; + } + indexInPool = CFArrayGetCount(_callbackOperationPool); + + if(!indexInPool) + { + [NSException raise:NSInvalidArgumentException format:@" Warning @2 :: Pool was sudently cleaned .... "]; + + break; + } + +// if( diispacthed && ![[self currentPoolOperation] valid] ) +// [NSThread sleepForTimeInterval:0.01]; + + // if( diispacthed + //// && !isRunningThreadMain && !isRunningThreadMain + // && [[self currentPoolOperation] valid] + // && [self connectionPoolOperationCount] > 1 + // && ! PG_busy ) + // break; + + PGConnectionStatus con_status = [((PGConnection*)self) status]; + if( con_status == PGConnectionStatusDisconnected || ! _connection) + break; + } +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" //// Clean **** :: %@ ", queued_name_STR); +#endif + + } @catch (NSException *exception) { + NSLog(@"**************************** \n ERROR :: %@ :: \n **************************** \n [ %@ ] \n **************************** \n ", NSStringFromSelector(_cmd), exception); + dispatch_semaphore_signal(sem); + } @finally { + + } + [[NSThread currentThread] cancel]; +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" //// END :: %@ ", queued_name_STR); +#endif + +} + +-(void)dispathCall +{ + + NSTimeInterval resolutionTimeOut = 0.05; + NSDate* theNextDate = [NSDate dateWithTimeIntervalSinceNow:resolutionTimeOut]; + bool isRunningThreadMain = YES; + bool isRunningThread = YES; + +// [NSThread sleepForTimeInterval:0.05];; + PGConnectionStatus con_status = [((PGConnection*)self) status]; + if( + ( + con_status != PGConnectionStatusBusy && + con_status != PGConnectionStatusConnected && + con_status != PGConnectionStatusConnecting + + ) + || ! _connection || !_socket) + return; + + { + if(_runloopsource){ + CFRunLoopSourceSignal(_runloopsource); + }else{ + return; + } + + CFRunLoopWakeUp(CFRunLoopGetMain()); + + if(_runloopsource){ + CFRunLoopSourceSignal(_runloopsource); + } + + CFRunLoopWakeUp(CFRunLoopGetCurrent()); + + dispatch_queue_t qu_inRun = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); + dispatch_queue_t qu_inRun_main = dispatch_get_main_queue(); + + NSRunLoop *qq_loop = [NSRunLoop currentRunLoop]; + NSRunLoop *qq_loop_main = [NSRunLoop mainRunLoop]; + + isRunningThread = [qq_loop runMode:NSRunLoopCommonModes beforeDate:theNextDate]; + if(!isRunningThread) + { + [NSThread sleepForTimeInterval:0.1];; + } + + if( qq_loop != [NSRunLoop mainRunLoop]){ + if([[self currentPoolOperation] poolIdentifier] == 0 ){ + [qq_loop runUntilDate:theNextDate]; +// if(!isRunningThread){ +// [qq_loop_main runUntilDate:theNextDate]; +// [NSThread sleepForTimeInterval:0.1];; +// } + + // [qq_loop_main runUntilDate:theNextDate]; + // + // isRunningThread = [qq_loop_main runMode:NSRunLoopCommonModes beforeDate:theNextDate]; + // if(!isRunningThread) + // { + // [NSThread sleepForTimeInterval:.1];; + // [self performSelectorOnMainThread:@selector(dispathCall) withObject:self waitUntilDone:YES modes:@[NSRunLoopCommonModes, NSDefaultRunLoopMode] ]; + // } + // + }else{ + + { +// [qq_loop_main runUntilDate:theNextDate]; + [qq_loop runUntilDate:theNextDate]; + + } +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" END performSelectorOnMainThread :: %@", NSStringFromSelector(_cmd)); +#endif + return; // don t care + } + }else + if([[self currentPoolOperation] valid]) + { + if(!isRunningThread){ + [qq_loop runUntilDate:theNextDate]; + }else{ +// [qq_loop_main runUntilDate:theNextDate]; + } + } + + } } @end diff --git a/src/Frameworks/PGClientKit/PGConnection+PGConnectionSocket.h b/src/Frameworks/PGClientKit/PGConnection+PGConnectionSocket.h new file mode 100644 index 0000000..9f6f597 --- /dev/null +++ b/src/Frameworks/PGClientKit/PGConnection+PGConnectionSocket.h @@ -0,0 +1,15 @@ +// +// PGConnection+PGConnectionSocket.h +// postgresql-kit +// +// Created by Cotillard Sebastien on 02/08/2017. +// +// + +#import +#import + +@interface PGConnection (PGConnectionSocket) + +-(void)__CFSocket_instanciate; +@end diff --git a/src/Frameworks/PGClientKit/PGConnection+PGConnectionSocket.m b/src/Frameworks/PGClientKit/PGConnection+PGConnectionSocket.m new file mode 100644 index 0000000..f18c9ac --- /dev/null +++ b/src/Frameworks/PGClientKit/PGConnection+PGConnectionSocket.m @@ -0,0 +1,417 @@ +// +// PGConnection+PGConnectionSocket.m +// postgresql-kit + +// ************************************* +// +// Copyright 2017 - ?? Sebastien Cotillard - Genose.org +// 07/2017 Sebastien Cotillard +// https://github.com/genose +// +// ************************************* +// ADDING Pool concurrent operation +// ************************************* +// ADDING Fraking CFSocket non-blocking Main Thread respons and concurrent operation +// ************************************* + +#import "PGConnection+PGConnectionSocket.h" + + +#include + +//#include "" +#include +#include +#include +#include +#include +#include +#include + + +extern void _CFRunLoopSourceWakeUpRunLoops(CFRunLoopSourceRef rls); +extern void __CFRunLoopSourceWakeUpLoop(const void*,void*); + +extern void _socketCallback; + +static CFMutableArrayRef __CFCF_CFAllSockets = NULL; + +#define INVALID_SOCKET (CFSocketNativeHandle)(-1) +#define MAX_SOCKADDR_LEN 256 +#define __CFCF_CFSockQueue() dispatch_get_current_queue() + +static int MAX_SOCKET = 100; +static int USED_SOCKET = 0; + +@implementation PGConnection (PGConnectionSocket) +void _CFRunLoopSourceWakeUpRunLoops(CFRunLoopSourceRef rls) { + return; + // CFBagRef loops = NULL; + // __CFRunLoopSourceLock(rls); + // if (__CFIsValid(rls) && NULL != rls->_runLoops) { + // loops = CFBagCreateCopy(kCFAllocatorSystemDefault, rls->_runLoops); + // } + // __CFRunLoopSourceUnlock(rls); + // if (loops) { + // CFBagApplyFunction(loops, __CFRunLoopSourceWakeUpLoop, NULL); + // CFRelease(loops); + // } +} +CFSocketRef _CFCF_CFSocketCreateWithNative(CFAllocatorRef allocator, CFSocketNativeHandle ufd, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context) { +#if defined(CHECK_FOR_FORK_RET) + CHECK_FOR_FORK_RET(NULL); +#endif + if(__CFCF_CFAllSockets == nil) + __CFCF_CFAllSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + CFSocketGetTypeID(); // cause initialization if necessary + + struct stat statbuf; + int ret = fstat(ufd, &statbuf); + if (ret < 0) ufd = INVALID_SOCKET; + + Boolean sane = false; + if (INVALID_SOCKET != ufd) { + uint32_t type = (statbuf.st_mode & S_IFMT); + sane = (S_IFSOCK == type) || (S_IFIFO == type) || (S_IFCHR == type); + if (0 && !sane) { + CFLog(kCFLogLevelWarning, CFSTR("*** _CFCF_CFSocketCreateWithNative(): creating CFSocket with silly fd type (%07o) -- may or may not work"), type); + } + } + + if (INVALID_SOCKET != ufd) { + Boolean canHandle = false; + int tmp_kq = kqueue(); + if (0 <= tmp_kq) { + struct kevent ev[2]; + EV_SET(&ev[0], ufd, EVFILT_READ, EV_ADD, 0, 0, 0); + EV_SET(&ev[1], ufd, EVFILT_WRITE, EV_ADD, 0, 0, 0); + int ret = kevent(tmp_kq, ev, 2, NULL, 0, NULL); + canHandle = (0 <= ret); // if kevent(ADD) succeeds, can handle + close(tmp_kq); + } + if (0 && !canHandle) { + CFLog(kCFLogLevelWarning, CFSTR("*** _CFCF_CFSocketCreateWithNative(): creating CFSocket with unsupported fd type -- may or may not work")); + } + } + + if (INVALID_SOCKET == ufd) { + // Historically, bad ufd was allowed, but gave an uncached and already-invalid CFSocketRef + SInt32 size = sizeof(struct __CFSocket) - sizeof(CFRuntimeBase); + CFSocketRef memory = (CFSocketRef)_CFRuntimeCreateInstance(allocator, CFSocketGetTypeID(), size, NULL); + if (NULL == memory) { + return NULL; + } + memory->_callout = callout; + memory->_state = kCFSocketStateInvalid; + return memory; + } + + __block CFSocketRef sock = NULL; + dispatch_sync(__CFCF_CFSockQueue(), ^{ + @try { + + for (CFIndex idx = 0, cnt = CFArrayGetCount(__CFCF_CFAllSockets); idx < cnt; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFCF_CFAllSockets, idx); + if (s->_shared->_socket == ufd) { + CFRetain(s); + sock = s; + return; + } + } + + SInt32 size = sizeof(struct __CFSocket) - sizeof(CFRuntimeBase); + __CF_CFSocketRef memory = malloc(sizeof(__CF_CFSocketRef)); + CFSocketRef cfcf_memory = (CFSocketRef)_CFRuntimeCreateInstance(allocator, CFSocketGetTypeID(), size, NULL); + if (NULL == memory) { + return; + } + + int socketType = 0; + if (INVALID_SOCKET != ufd) { + socklen_t typeSize = sizeof(socketType); + int ret = getsockopt(ufd, SOL_SOCKET, SO_TYPE, (void *)&socketType, (socklen_t *)&typeSize); + if (ret < 0) socketType = 0; + } + + ((CFSocketRef)cfcf_memory)->_error = 0; + + memory->_rsuspended = true; + memory->_wsuspended = true; + memory->_readable = false; + memory->_writeable = false; + + memory->_isSaneFD = sane ? 1 : 0; + memory->_wantReadType = (callBackTypes & 0x3); + memory->_reenableRead = memory->_wantReadType ? true : false; + memory->_readDisabled = false; + memory->_wantWrite = (callBackTypes & kCFSocketWriteCallBack) ? true : false; + memory->_reenableWrite = false; + memory->_writeDisabled = false; + memory->_wantConnect = (callBackTypes & kCFSocketConnectCallBack) ? true : false; + memory->_connectDisabled = false; + memory->_leaveErrors = false; + memory->_closeOnInvalidate = true; + memory->_connOriented = (SOCK_STREAM == socketType || SOCK_SEQPACKET == socketType); + memory->_connected = (memory->_wantReadType == kCFSocketAcceptCallBack || !memory->_connOriented) ? true : false; + + memory->_error = 0; + memory->_runLoopCounter = 0; + memory->_address = NULL; + memory->_peerAddress = NULL; + memory->_context.info = NULL; + memory->_context.retain = NULL; + memory->_context.release = NULL; + memory->_context.copyDescription = NULL; + memory->_callout = callout; + if (NULL != context) { + objc_memmove_collectable(&memory->_context, context, sizeof(CFSocketContext)); + memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info; + } + + struct __shared_blob *shared = malloc(sizeof(struct __shared_blob)); + shared->_rdsrc = NULL; + shared->_wrsrc = NULL; + shared->_source = NULL; + shared->_socket = ufd; + shared->_closeFD = true; // copy of _closeOnInvalidate + shared->_refCnt = 1; // one for the CFSocket + memory->_shared = shared; + + if (memory->_wantReadType) { + dispatch_source_t dsrc = NULL; + if (sane != nil) { + dsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, ufd, 0, __CFCF_CFSockQueue()); + } else { + dsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, __CFCF_CFSockQueue()); + dispatch_source_set_timer(dsrc, dispatch_time(DISPATCH_TIME_NOW, 0), NSEC_PER_SEC / 2, NSEC_PER_SEC); + } + dispatch_block_t event_block = ^{ + memory->_readable = true; + if (!memory->_rsuspended) { + dispatch_suspend(dsrc); + memory->_rsuspended = true; + } + if (shared->_source) { + CFRunLoopSourceSignal(shared->_source); + _CFRunLoopSourceWakeUpRunLoops(shared->_source); + } + }; + dispatch_block_t cancel_block = ^{ + shared->_rdsrc = NULL; + shared->_refCnt--; + if (0 == shared->_refCnt) { + if (shared->_closeFD) close(shared->_socket); + free(shared); + } + // dispatch_release(dsrc); + }; + dispatch_source_set_event_handler(dsrc, event_block); + dispatch_source_set_cancel_handler(dsrc, cancel_block); + shared->_rdsrc = dsrc; + } + if (memory->_wantWrite || memory->_wantConnect) { + dispatch_source_t dsrc = NULL; + if (sane != nil) { + dsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, ufd, 0, __CFCF_CFSockQueue()); + } else { + dsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, __CFCF_CFSockQueue()); + dispatch_source_set_timer(dsrc, dispatch_time(DISPATCH_TIME_NOW, 0), NSEC_PER_SEC / 2, NSEC_PER_SEC); + } + dispatch_block_t event_block = ^{ + memory->_writeable = true; + if (!memory->_wsuspended) { + dispatch_suspend(dsrc); + memory->_wsuspended = true; + } + if (shared->_source) { + CFRunLoopSourceSignal(shared->_source); + _CFRunLoopSourceWakeUpRunLoops(shared->_source); + } + }; + dispatch_block_t cancel_block = ^{ + shared->_wrsrc = NULL; + shared->_refCnt--; + if (0 == shared->_refCnt) { + if (shared->_closeFD) close(shared->_socket); + free(shared); + } + // dispatch_release(dsrc); + }; + dispatch_source_set_event_handler(dsrc, event_block); + dispatch_source_set_cancel_handler(dsrc, cancel_block); + shared->_wrsrc = dsrc; + } + + if (shared->_rdsrc) { + shared->_refCnt++; + } + if (shared->_wrsrc) { + shared->_refCnt++; + } + + memory->_state = kCFSocketStateReady; + CFIndex indexInPool = CFArrayGetCount(__CFCF_CFAllSockets); + + CFArrayAppendValue(__CFCF_CFAllSockets, cfcf_memory); + sock = memory; + + } @catch (NSException *exception) { + NSLog(@" :::::: Error :: %@ ", exception ); + } @finally { + + } + }); + CFLog(5, CFSTR("_CFCF_CFSocketCreateWithNative(): created socket %p with callbacks 0x%x"), sock, callBackTypes); + return sock; +} + + + + +-(CFSocketRef)__CFSocket_instanciate +{ + + // create socket object + CFSocketContext context = {0, (__bridge void* )(self), NULL, NULL, NULL}; + + // _socket = CFSocketCreate(kCFAllocatorDefault, 0, 0, 0, kCFSocketReadCallBack | kCFSocketWriteCallBack, &_socketCallback,&context); + + // PQsocket(_connection); + if( _connection == nil ) + { + [NSException raise:NSInvalidArgumentException format:@" Error :: Can't create _connection : %@ ", self]; + return; + } + + + if( !PQsocket(_connection) ) + { + [NSException raise:NSInvalidArgumentException format:@" Error :: Can't create _connection : %@ ", self]; + return; + } + + if(_socket) + { + [self _socketDisconnect]; + } + + + int timeout = 60; + + while (! _socket && timeout > 0 ) + { + + _socket = CFSocketCreateWithNative(kCFAllocatorDefault,PQsocket(_connection),kCFSocketReadCallBack | kCFSocketWriteCallBack,&_socketCallback,&context); + [NSThread sleepForTimeInterval:0.25]; + + timeout --; + if(!timeout) + [NSException raise:NSInvalidArgumentException format:@" Error :: Can't create _connection :: TIMEOUT : %@ ", self]; + + if(!_socket) + { + NSLog(@" %@ :: %p :: Connecting ... ", NSStringFromClass([self class]), self); + }else{ + NSLog(@" %@ :: %p :: Socket aquired (%p) ... ", NSStringFromClass([self class]), self, _socket); + } + + } + + +} + + +//////////////////////////////////////////////////////////////////////////////// +#pragma mark private methods - socket connect/disconnect +//////////////////////////////////////////////////////////////////////////////// + +-(void)_socketConnect:(PGConnectionState)state { + + NSParameterAssert(state==PGConnectionStateConnect || state==PGConnectionStateReset || state==PGConnectionStateNone); + NSParameterAssert(_connection != nil); + + if(USED_SOCKET > MAX_SOCKET) + { + return; + } +// NSParameterAssert(_state==PGConnectionStateNone); +// NSParameterAssert(_socket==nil && _runloopsource==nil); + [self __CFSocket_instanciate]; + + if(_socket == nil) + { + [NSException raise:NSInvalidArgumentException format:@" Error :: Can't create socket : %@ ", self]; + return; + } + + + // _socket = CFSocketCreate(kCFAllocatorDefault, 0, 0, 0, kCFSocketReadCallBack | kCFSocketWriteCallBack,&_socketCallback,&context); + +// NSParameterAssert(_socket && CFSocketIsValid(_socket)); + // let libpq do the socket closing + CFSocketSetSocketFlags(_socket,~kCFSocketCloseOnInvalidate & CFSocketGetSocketFlags(_socket)); + // CFSocketEnableCallBacks(_socket, kCFSocketDataCallBack | kCFSocketReadCallBack | kCFSocketWriteCallBack); + +// dispatch_queue_t qu_inRun = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); +// +// dispatch_source_t dsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, qu_inRun ); +// +// dispatch_source_set_timer(dsrc, dispatch_time(DISPATCH_TIME_NOW, 0), NSEC_PER_SEC / 2, NSEC_PER_SEC); + + // cf(_socket, F_SETFL, O_NONBLOCK); + + // set state + [self setState:state]; + [self _updateStatus]; + + if(!_socket) + return ; + // add to run loop to begin polling + _runloopsource = CFSocketCreateRunLoopSource(NULL,_socket,1); + NSParameterAssert(_runloopsource && CFRunLoopSourceIsValid(_runloopsource)); + CFRunLoopAddSource( + // CFRunLoopGetMain(), + CFRunLoopGetCurrent(), // One connection PER Thread + _runloopsource,(CFStringRef)kCFRunLoopCommonModes); + + dispatch_semaphore_t semaphore_query_send = [[self masterPoolOperation] semaphore]; +// [self dispathCall]; +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"%@ :: %@ :::: Socket created ....", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); +#endif + + USED_SOCKET ++; + [self wait_semaphore_read: semaphore_query_send ]; + + [NSThread sleepForTimeInterval:0.05]; + + [[NSThread currentThread] cancel]; + + [NSThread sleepForTimeInterval:0.05]; + + // CFRunLoopRun();//(kCFRunLoopDefaultMode, 0.2 , NO); +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" ------- %@ :: %@ :::: Socket Runloop ENDED CLEAR ....", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); +#endif +} + +-(void)_socketDisconnect { + [NSThread sleepForTimeInterval:0.05]; + if(_runloopsource) { + CFRunLoopSourceInvalidate(_runloopsource); + CFRelease(_runloopsource); + _runloopsource = nil; + } + [NSThread sleepForTimeInterval:0.1]; + if(_socket) { + CFSocketInvalidate(_socket); + [NSThread sleepForTimeInterval:0.05]; + CFRelease(_socket); + [NSThread sleepForTimeInterval:0.05]; + _socket = nil; + USED_SOCKET --; + } + [NSThread sleepForTimeInterval:0.05]; +} + +@end diff --git a/src/Frameworks/PGClientKit/PGConnection.h b/src/Frameworks/PGClientKit/PGConnection.h index 74a1136..6c2f638 100644 --- a/src/Frameworks/PGClientKit/PGConnection.h +++ b/src/Frameworks/PGClientKit/PGConnection.h @@ -21,6 +21,8 @@ * and errors. */ + + //////////////////////////////////////////////////////////////////////////////// // constants @@ -75,19 +77,32 @@ typedef enum { PGClientTupleFormatBinary = 1 } PGClientTupleFormat; +typedef enum { + PGOperationStateNone = 0, + PGOperationStatePrepare = 1, + PGOperationStateBusy = 4 +} PGOperationState; + +@class PGConnectionOperation; + //////////////////////////////////////////////////////////////////////////////// // PGConnection interface -@interface PGConnection : NSObject { +@interface PGConnection : NSObject { void* _connection; + bool _connectionClosed; void* _cancel; - void* _callback; +// replaced by a pool Mechanism :: void* _callback; + void* _callbackOperation; + CFMutableArrayRef _callbackOperationPool; CFSocketRef _socket; CFRunLoopSourceRef _runloopsource; NSUInteger _timeout; PGConnectionState _state; + PGOperationState _stateOperation; NSDictionary* _parameters; PGClientTupleFormat _tupleFormat; + id _connectedUrl; } //////////////////////////////////////////////////////////////////////////////// @@ -176,12 +191,35 @@ typedef enum { -(NSString* )quoteString:(NSString* )string; -(NSString* )encryptedPassword:(NSString* )password role:(NSString* )roleName; + + +-(NSInteger)currentPoolOperationChildCount; +-(NSInteger)connectionPoolOperationCount; + +-(id)addOperation:(id)operationClass withCallBackWhenDone:(void*)callBackWhenDone withCallBackWhenError:(void*)callBackWhenError; + +-(PGConnectionOperation*)prevPoolOperation; +-(PGConnectionOperation*)currentPoolOperation; +-(PGConnectionOperation*)masterPoolOperation; + +-(id)invalidateOperation:(NSInteger)operationRefIndex; +-(void)_waitingPoolOperationForResult; +-(void)_waitingPoolOperationForResultMaster; + +-(void)wait_semaphore_read:(dispatch_semaphore_t) sem; +-(void)wait_semaphore_read:(dispatch_semaphore_t) sem withQueue:(dispatch_queue_t)qq_in; +-(void)dispathCall; @end //////////////////////////////////////////////////////////////////////////////// @interface PGConnection (Connect) + + +-(void) _reconnectWithHandler: ( void(^ _Nullable )(void * pm,NSError* error)) callback; + + /** * Connect to a database (as specififed by the URL) without blocking. The method * returns immediately, on initiation of the connection. Once the connection @@ -263,8 +301,10 @@ typedef enum { * * @param query Either an NSString or PGQuery object * @param callback The callback which is called on completion of the execution + * + * @return The (id ?? shall be Transversable ?? ) object containing results of the query */ --(void)execute:(id)query whenDone:(void(^)(PGResult* result,NSError* error)) callback; +-(id)execute:(id)query whenDone:(void(^)(PGResult* result,NSError* error)) callback; /** * This method execute a statement on the server syncronously, then returns @@ -275,7 +315,7 @@ typedef enum { * @param query Either an NSString or PGQuery object * @param error A pointer to an error object to be returned on error * - * @return The RGResult object containing results of the query + * @return The PGResult object containing results of the query */ -(PGResult* )execute:(id)query error:(NSError** )error; diff --git a/src/Frameworks/PGClientKit/PGConnection.m b/src/Frameworks/PGClientKit/PGConnection.m index 47aebd3..2dbd983 100644 --- a/src/Frameworks/PGClientKit/PGConnection.m +++ b/src/Frameworks/PGClientKit/PGConnection.m @@ -12,6 +12,16 @@ // License for the specific language governing permissions and limitations // under the License. +// ************************************* +// +// Copyright 2017 - ?? Sebastien Cotillard - Genose.org +// 07/2017 Sebastien Cotillard +// https://github.com/genose +// +// ************************************* +// ADDING Pool concurrent operation +// ************************************* + #import #import #include @@ -48,11 +58,11 @@ @implementation PGConnection //////////////////////////////////////////////////////////////////////////////// +(NSArray* )allURLSchemes { - return [PGConnectionSchemes componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + return [PGConnectionSchemes componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; } +(NSString* )defaultURLScheme { - return [[self allURLSchemes] objectAtIndex:0]; + return [[self allURLSchemes] objectAtIndex:0]; } //////////////////////////////////////////////////////////////////////////////// @@ -62,22 +72,31 @@ +(NSString* )defaultURLScheme { -(instancetype)init { self = [super init]; if(self) { - _connection = nil; - _cancel = nil; - _callback = nil; - _socket = nil; - _runloopsource = nil; - _timeout = 0; - _state = PGConnectionStateNone; - _parameters = nil; - _tupleFormat = PGClientTupleFormatText; - pgdata2obj_init(); // set up cache for translating binary data from server + _connection = nil; + _cancel = nil; + // _callback = nil; + _stateOperation = PGOperationStateNone; + _callbackOperation = nil; + _callbackOperationPool = CFArrayCreateMutable(NULL, 64, &kCFTypeArrayCallBacks); + // (NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + // CFArrayInsertValueAtIndex(_callbackOperationPool, 0, CFBridgingRetain([[PGConnectionOperation alloc]init])); + + // (_callbackOperationPool, CFSTR("A String Key"), CFBridgingRetain([PGConnectionOperation new])); + // :: dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + _socket = nil; + _runloopsource = nil; + _timeout = 0; + _state = PGConnectionStateNone; + _parameters = nil; + _tupleFormat = PGClientTupleFormatText; + pgdata2obj_init(); // set up cache for translating binary data from server } return self; } -(void)finalize { - [self disconnect]; + [self disconnect]; } //////////////////////////////////////////////////////////////////////////////// @@ -95,127 +114,127 @@ -(void)finalize { @synthesize tupleFormat = _tupleFormat; -(PGConnectionStatus)status { - if(_connection==nil) { - return PGConnectionStatusDisconnected; - } - switch(PQstatus(_connection)) { - case CONNECTION_OK: - return [self state]==PGConnectionStateNone ? PGConnectionStatusConnected : PGConnectionStatusBusy; - case CONNECTION_STARTED: - case CONNECTION_MADE: - case CONNECTION_AWAITING_RESPONSE: - case CONNECTION_AUTH_OK: - case CONNECTION_SSL_STARTUP: - case CONNECTION_SETENV: - return PGConnectionStatusConnecting; - default: - return PGConnectionStatusRejected; - } + if(_connection==nil) { + return PGConnectionStatusDisconnected; + } + switch(PQstatus(_connection)) { + case CONNECTION_OK: + return [self state]==PGConnectionStateNone ? PGConnectionStatusConnected : PGConnectionStatusBusy; + case CONNECTION_STARTED: + case CONNECTION_MADE: + case CONNECTION_AWAITING_RESPONSE: + case CONNECTION_AUTH_OK: + case CONNECTION_SSL_STARTUP: + case CONNECTION_SETENV: + return PGConnectionStatusConnecting; + default: + return PGConnectionStatusRejected; + } } -(NSString* )user { - if(_connection==nil || PQstatus(_connection) != CONNECTION_OK) { - return nil; - } - return [NSString stringWithUTF8String:PQuser(_connection)]; + if(_connection==nil || PQstatus(_connection) != CONNECTION_OK) { + return nil; + } + return [NSString stringWithUTF8String:PQuser(_connection)]; } -(NSString* )database { - if(_connection==nil || PQstatus(_connection) != CONNECTION_OK) { - return nil; - } - return [NSString stringWithUTF8String:PQdb(_connection)]; + if(_connection==nil || PQstatus(_connection) != CONNECTION_OK) { + return nil; + } + return [NSString stringWithUTF8String:PQdb(_connection)]; } -(NSString* )host { - if(_connection==nil || PQstatus(_connection) != CONNECTION_OK) { - return nil; - } - const char* host = PQhost(_connection); - if(host) { - return [NSString stringWithUTF8String:host]; - } else { - return nil; - } + if(_connection==nil || PQstatus(_connection) != CONNECTION_OK) { + return nil; + } + const char* host = PQhost(_connection); + if(host) { + return [NSString stringWithUTF8String:host]; + } else { + return nil; + } } -(int)serverProcessID { - if(_connection==nil || PQstatus(_connection) != CONNECTION_OK) { - return 0; - } - return PQbackendPID(_connection); + if(_connection==nil || PQstatus(_connection) != CONNECTION_OK) { + return 0; + } + return PQbackendPID(_connection); } -(NSDictionary* )parameters { - if(_parameters != nil) { - return _parameters; - } - NSMutableDictionary* parameters = [NSMutableDictionary dictionary]; - NSParameterAssert(parameters); - _parameters = parameters; - - // populate parameters from bundle - NSBundle* bundle = [NSBundle bundleForClass:[self class]]; - for(NSString* key in @[ @"CFBundleIdentifier", @"CFBundleName", @"CFBundleShortVersionString" ]) { - id value = [[bundle infoDictionary] objectForKey:key]; - if(value) { - [parameters setObject:value forKey:key]; - } - } - - // populate parameters from connection - if(_connection) { - - // server version - int serverVersion = PQserverVersion(_connection); - if(serverVersion) { - [parameters setObject:[NSNumber numberWithInt:serverVersion] forKey:PGConnectionServerVersionKey]; - [parameters setObject:[NSNumber numberWithInt:(serverVersion / 10000) % 10] forKey:PGConnectionServerMajorVersionKey]; - [parameters setObject:[NSNumber numberWithInt:(serverVersion / 100) % 10] forKey:PGConnectionServerMinorVersionKey]; - [parameters setObject:[NSNumber numberWithInt:(serverVersion / 1) % 10] forKey:PGConnectionServerRevisionVersionKey]; - } - - // protocol version and pid - NSNumber* protocol = [NSNumber numberWithInt:PQprotocolVersion(_connection)]; - NSNumber* pid = [NSNumber numberWithInt:[self serverProcessID]]; - if(protocol) { - [parameters setObject:protocol forKey:PGConnectionProtocolVersionKey]; - } - if(pid) { - [parameters setObject:pid forKey:PGConnectionServerProcessKey]; - } - - // user, database & host - NSString* user = [self user]; - NSString* database = [self database]; - NSString* host = [self host]; - if(user) { - [parameters setObject:user forKey:PGConnectionUserKey]; - } - if(database) { - [parameters setObject:database forKey:PGConnectionDatabaseKey]; - } - if(host) { - [parameters setObject:database forKey:PGConnectionHostKey]; - } - - // ssl - SSL* ssl = PQgetssl(_connection); - if(ssl) { - [parameters setObject:[NSNumber numberWithInt:ssl->version] forKey:PGConnectionSSLVersionKey]; - } - - // other server parameters - for(NSString* key in @[ @"server_version", @"server_encoding", @"client_encoding", @"application_name", @"is_superuser", @"session_authorization", @"DateStyle", @"IntervalStyle", @"TimeZone", @"integer_datetimes", @"standard_conforming_strings"]) { - const char* value = PQparameterStatus(_connection,[key UTF8String]); - if(value) { - [parameters setObject:[NSString stringWithUTF8String:value] forKey:key]; - } - } - } - - // return parameters - return parameters; + if(_parameters != nil) { + return _parameters; + } + NSMutableDictionary* parameters = [NSMutableDictionary dictionary]; + NSParameterAssert(parameters); + _parameters = parameters; + + // populate parameters from bundle + NSBundle* bundle = [NSBundle bundleForClass:[self class]]; + for(NSString* key in @[ @"CFBundleIdentifier", @"CFBundleName", @"CFBundleShortVersionString" ]) { + id value = [[bundle infoDictionary] objectForKey:key]; + if(value) { + [parameters setObject:value forKey:key]; + } + } + + // populate parameters from connection + if(_connection) { + + // server version + int serverVersion = PQserverVersion(_connection); + if(serverVersion) { + [parameters setObject:[NSNumber numberWithInt:serverVersion] forKey:PGConnectionServerVersionKey]; + [parameters setObject:[NSNumber numberWithInt:(serverVersion / 10000) % 10] forKey:PGConnectionServerMajorVersionKey]; + [parameters setObject:[NSNumber numberWithInt:(serverVersion / 100) % 10] forKey:PGConnectionServerMinorVersionKey]; + [parameters setObject:[NSNumber numberWithInt:(serverVersion / 1) % 10] forKey:PGConnectionServerRevisionVersionKey]; + } + + // protocol version and pid + NSNumber* protocol = [NSNumber numberWithInt:PQprotocolVersion(_connection)]; + NSNumber* pid = [NSNumber numberWithInt:[self serverProcessID]]; + if(protocol) { + [parameters setObject:protocol forKey:PGConnectionProtocolVersionKey]; + } + if(pid) { + [parameters setObject:pid forKey:PGConnectionServerProcessKey]; + } + + // user, database & host + NSString* user = [self user]; + NSString* database = [self database]; + NSString* host = [self host]; + if(user) { + [parameters setObject:user forKey:PGConnectionUserKey]; + } + if(database) { + [parameters setObject:database forKey:PGConnectionDatabaseKey]; + } + if(host) { + [parameters setObject:database forKey:PGConnectionHostKey]; + } + + // ssl + SSL* ssl = PQgetssl(_connection); + if(ssl) { + [parameters setObject:[NSNumber numberWithInt:ssl->version] forKey:PGConnectionSSLVersionKey]; + } + + // other server parameters + for(NSString* key in @[ @"server_version", @"server_encoding", @"client_encoding", @"application_name", @"is_superuser", @"session_authorization", @"DateStyle", @"IntervalStyle", @"TimeZone", @"integer_datetimes", @"standard_conforming_strings"]) { + const char* value = PQparameterStatus(_connection,[key UTF8String]); + if(value) { + [parameters setObject:[NSString stringWithUTF8String:value] forKey:key]; + } + } + } + + // return parameters + return parameters; } //////////////////////////////////////////////////////////////////////////////// @@ -223,100 +242,342 @@ -(NSDictionary* )parameters { //////////////////////////////////////////////////////////////////////////////// -(void)_updateStatusDelayed:(NSArray* )arguments { - NSParameterAssert(arguments && [arguments count]==2); - PGConnectionStatus status = [[arguments objectAtIndex:0] intValue]; - NSString* description = [arguments objectAtIndex:1]; - if([[self delegate] respondsToSelector:@selector(connection:statusChange:description:)]) { - [[self delegate] connection:self statusChange:status description:description]; - } + NSParameterAssert(arguments && [arguments count]==2); + PGConnectionStatus status = [[arguments objectAtIndex:0] intValue]; + NSString* description = [arguments objectAtIndex:1]; + if([[self delegate] respondsToSelector:@selector(connection:statusChange:description:)]) { + [[self delegate] connection:self statusChange:status description:description]; + } + [[NSThread currentThread] cancel]; + // [NSThread cancelPreviousPerformRequestsWithTarget:self]; } -(void)_updateStatus { - static PGConnectionStatus oldStatus = PGConnectionStatusDisconnected; - static dispatch_once_t onceToken; +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@"PGConnection (%p) - _updateStatus ",self); +#endif + static PGConnectionStatus oldStatus = PGConnectionStatusDisconnected; + static dispatch_once_t onceToken; dispatch_once(&onceToken,^{ // Do some work that happens once - PGConnectionStatusDescription = @{ - [NSNumber numberWithInt:PGConnectionStatusBusy]: @"Busy", - [NSNumber numberWithInt:PGConnectionStatusConnected]: @"Idle", - [NSNumber numberWithInt:PGConnectionStatusConnecting]: @"Connecting", - [NSNumber numberWithInt:PGConnectionStatusDisconnected]: @"Disconnected", - [NSNumber numberWithInt:PGConnectionStatusRejected]: @"Rejected" - }; + PGConnectionStatusDescription = @{ + [NSNumber numberWithInt:PGConnectionStatusBusy]: @"Busy", + [NSNumber numberWithInt:PGConnectionStatusConnected]: @"Idle", + [NSNumber numberWithInt:PGConnectionStatusConnecting]: @"Connecting", + [NSNumber numberWithInt:PGConnectionStatusDisconnected]: @"Disconnected", + [NSNumber numberWithInt:PGConnectionStatusRejected]: @"Rejected" + }; }); - if([self status] == oldStatus) { - return; - } - // reset oldStatus - oldStatus = [self status]; - - // we call the delegate in a delayed fashion, so as to not stop the callbacks - // from continuing - NSNumber* key = [NSNumber numberWithInt:oldStatus]; - NSString* description = [PGConnectionStatusDescription objectForKey:key]; - [self performSelectorOnMainThread:@selector(_updateStatusDelayed:) withObject:@[ key,description ] waitUntilDone:NO]; -#ifdef DEBUG2 - NSLog(@"status => %@ %@",key,description); + if([self status] == oldStatus) { + return; + } + // reset oldStatus + oldStatus = [self status]; + + // we call the delegate in a delayed fashion, so as to not stop the callbacks + // from continuing + NSNumber* key = [NSNumber numberWithInt:oldStatus]; + NSString* description = [PGConnectionStatusDescription objectForKey:key]; + // [self performSelectorOnMainThread:@selector(_updateStatusDelayed:) withObject:@[ key,description ] waitUntilDone:NO]; + [self performSelector:@selector(_updateStatusDelayed:) withObject:@[ key,description ] ]; +#if defined(DEBUG2) && DEBUG2 == 1 + NSLog(@"status => %@ %@",key,description); #endif + + // if connection is rejected, then call disconnect + if(oldStatus==PGConnectionStatusRejected) { +#if defined(DEBUG2) && DEBUG2 == 1 + NSLog(@"_updateStatus => disconnect/rejected :: %@ %@",key,description); +#endif + [self disconnect]; + } +} + + + +-(NSInteger)currentPoolOperationChildCount +{ + CFIndex indexInPool = CFArrayGetCount(_callbackOperationPool); + return indexInPool; +} - // if connection is rejected, then call disconnect - if(oldStatus==PGConnectionStatusRejected) { - [self disconnect]; - } +-(NSInteger)connectionPoolOperationCount +{ + CFIndex indexInPool = CFArrayGetCount(_callbackOperationPool); + return indexInPool; +} +-(PGConnectionOperation*)masterPoolOperation +{ + PGConnectionOperation* masterPoolOperation = nil; + + @synchronized (self) { + + CFIndex indexInPool = CFArrayGetCount(_callbackOperationPool); + if(indexInPool > 0 ) + { + masterPoolOperation = (__bridge PGConnectionOperation*) CFArrayGetValueAtIndex(_callbackOperationPool, 0); + }else{ + // NSLog(@" ERROR :: NO master pool .... "); + if(_socket || _connection) + [NSException raise:NSInvalidArgumentException format:@" ERROR :: NO master pool .... "]; + } + + } +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + // NSLog(@" %@::%@ :: FETCH cureent pool (%d :: %@ ) .... ", NSStringFromClass([self class]), NSStringFromSelector(_cmd), indexInPool-1, masterPoolOperation); +#endif + + + return masterPoolOperation; +} + +-(PGConnectionOperation*)currentPoolOperation +{ + PGConnectionOperation* masterPoolOperation = nil; + @synchronized (self) { + + CFIndex indexInPool = CFArrayGetCount(_callbackOperationPool); + if(indexInPool > 0 ) + { + masterPoolOperation = (__bridge PGConnectionOperation*) CFArrayGetValueAtIndex(_callbackOperationPool, ((indexInPool-1) >=0 ? indexInPool-1 : 0) ); + }else{ + // NSLog(@" ERROR :: NO pool .... "); + if(! _connectionClosed ) + [NSException raise:NSInvalidArgumentException format:@" ERROR :: NO pool .... "]; + } + + } +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + // NSLog(@" %@::%@ :: FETCH cureent pool (%d :: %@ ) .... ", NSStringFromClass([self class]), NSStringFromSelector(_cmd), indexInPool-1, masterPoolOperation); +#endif + + + return masterPoolOperation; +} + + +-(PGConnectionOperation*)prevPoolOperation +{ + PGConnectionOperation* masterPoolOperation = nil; + + CFIndex indexInPool = CFArrayGetCount(_callbackOperationPool); + if(indexInPool > 1 ) + { + masterPoolOperation = (__bridge PGConnectionOperation*) CFArrayGetValueAtIndex(_callbackOperationPool, indexInPool-2); + }else{ + NSLog(@" ERROR :: NO PREV pool .... "); + return [self currentPoolOperation]; + } +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + // NSLog(@" %@::%@ :: FETCH prev pool (%d :: %@ ) .... ", NSStringFromClass([self class]), NSStringFromSelector(_cmd), indexInPool-1, masterPoolOperation); +#endif + + + return masterPoolOperation; +} +-(id)addOperation: (id)operationClass withCallBackWhenDone:(void*)callBackWhenDone withCallBackWhenError:(void*)callBackWhenError +{ + void * recall_callbackDone = (__bridge void *)((__bridge void (^)(void* result, void* error)) (callBackWhenDone)); + + + // callBackWhenDone(result, error); + // [[ ((PGConnectionOperation*)self) getConnectionDelegate] invalidateOperation: [((PGConnectionOperation*)self) poolIdentifier] ]; + + + CFIndex indexInPool = CFArrayGetCount(_callbackOperationPool); + + PGConnectionOperation *newPool = [[PGConnectionOperation alloc] initWithParametersDelegate: self withRefPoolIdentifier: indexInPool refClassOperation:operationClass callWhenDone: callBackWhenDone callWhenError: (__bridge void (^)(__strong id, NSError *__strong))(callBackWhenError)]; + + CFArrayAppendValue(_callbackOperationPool, CFBridgingRetain(newPool)); + + indexInPool = CFArrayGetCount(_callbackOperationPool); + id obj = CFArrayGetValueAtIndex(_callbackOperationPool, indexInPool-1); +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" %@::%@ :: ADDED pool (%d :: %@ ) .... ", NSStringFromClass([self class]), NSStringFromSelector(_cmd), indexInPool-1, [obj description]); +#endif + + return nil; +} +-(id)invalidateOperation:(NSInteger)operationRefIndex +{ + @try + { + + CFIndex indexInPool = CFArrayGetCount(_callbackOperationPool); + if(indexInPool > 0 && indexInPool>=operationRefIndex && operationRefIndex !=0) + { + id obj = CFArrayGetValueAtIndex(_callbackOperationPool, operationRefIndex); + dispatch_semaphore_t opeSemaphore = [obj semaphore]; + + [obj finish]; + + + //#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" %@ (%p) :: %@ :: REMOVED pool (%d :: %p :: %@ ) .... ", NSStringFromClass([self class]), self, NSStringFromSelector(_cmd), operationRefIndex, obj, [obj description]); + //#endif + indexInPool = CFArrayGetCount(_callbackOperationPool); + if( indexInPool >= operationRefIndex ) + { + CFArrayRemoveValueAtIndex(_callbackOperationPool, operationRefIndex); + }else{ + NSLog(@" %@ (%p) :: %@ :: ERROR REMOVing pool (%d) .... ", NSStringFromClass([self class]), self, NSStringFromSelector(_cmd), operationRefIndex); + } + + if(opeSemaphore) + dispatch_semaphore_signal( opeSemaphore ); + + } + }@catch (NSException *exception) { + NSLog(@" %@ exeception .... %@",NSStringFromSelector(_cmd),exception); + } + @finally{} + + return nil; } +//-(void)_waitingPoolOperationForResult +//{ +// PGConnectionOperation* prevPoolOpe = [self prevPoolOperation]; +// PGConnectionOperation* currentPoolOpe = [self currentPoolOperation]; +// +// NSLog(@" Waiting for concurrent queries resultset (%lu) (%@) ...... ", [self connectionPoolOperationCount], [self currentPoolOperation]); +// NSTimeInterval resolutionTimeOut = 0.5; +// bool isRunning = NO; +// while (1) { +// +// +// prevPoolOpe = [self prevPoolOperation]; +// currentPoolOpe = [self currentPoolOperation]; +// +// +// +// +// +// if( +// ([((PGConnectionOperation*)currentPoolOpe) valid] && [((PGConnectionOperation*)currentPoolOpe) poolIdentifier] !=0 ) +// // && [((PGConnectionOperation*)currentPoolOpe) poolIdentifier] !=0 +// || (_stateOperation) +// ) +// { +// NSDate* theNextDate = [NSDate dateWithTimeIntervalSinceNow:resolutionTimeOut]; +// isRunning = [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:theNextDate]; +// isRunning = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:theNextDate]; +// [NSThread sleepForTimeInterval:0.02]; +// // NSLog(@" >>>>> :::: %@ .... %d",NSStringFromSelector(_cmd),isRunning); +// +// // return; +// }else{ +// +// break; +// } +// } +// NSLog(@" CLEARED Waiting for concurrent queries resultset (%lu) (%@) ...... ", [self connectionPoolOperationCount], [self currentPoolOperation]); +//} +//-(void)_waitingPoolOperationForResultMaster +//{ +// NSTimeInterval resolutionTimeOut = 0.5; +// bool isRunning = NO; +// +// PGConnectionOperation* prevPoolOpe = [self prevPoolOperation]; +// PGConnectionOperation* currentPoolOpe = [self currentPoolOperation]; +// +// while ([((PGConnectionOperation*)currentPoolOpe) poolIdentifier] !=0) { +// +// +// prevPoolOpe = [self prevPoolOperation]; +// currentPoolOpe = [self currentPoolOperation]; +// +// +// +// if([((PGConnectionOperation*)prevPoolOpe) valid] +// && [((PGConnectionOperation*)currentPoolOpe) valid] +// ) +// { +// // NSDate* theNextDate = [NSDate dateWithTimeIntervalSinceNow:resolutionTimeOut]; +// // isRunning = [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:theNextDate]; +// [NSThread sleepForTimeInterval:0.02]; +// NSLog(@" >>>>> master wait :::: %@ .... %d",NSStringFromSelector(_cmd),isRunning); +// +// // return; +// }else{ +// NSLog(@" >>>>> master wait :: done :::: .... %d",isRunning); +// break; +// } +// } +// +// +//} + + //////////////////////////////////////////////////////////////////////////////// #pragma mark public methods - quoting //////////////////////////////////////////////////////////////////////////////// -(NSString* )quoteIdentifier:(NSString* )string { - if(_connection==nil) { - return nil; - } - - // if identifier only contains alphanumberic characters, return it unmodified - if([string isAlphanumericOrUnderscore]) { - return string; - } - - const char* quoted_identifier = PQescapeIdentifier(_connection,[string UTF8String],[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); - if(quoted_identifier==nil) { - return nil; - } - NSString* quoted_identifier2 = [NSString stringWithUTF8String:quoted_identifier]; - NSParameterAssert(quoted_identifier2); - PQfreemem((void* )quoted_identifier); - return quoted_identifier2; + if(_connection==nil) { + return nil; + } + + // if identifier only contains alphanumberic characters, return it unmodified + if([string isAlphanumericOrUnderscore]) { + return string; + } + + const char* quoted_identifier = PQescapeIdentifier(_connection,[string UTF8String],[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + if(quoted_identifier==nil) { + return nil; + } + NSString* quoted_identifier2 = [NSString stringWithUTF8String:quoted_identifier]; + NSParameterAssert(quoted_identifier2); + PQfreemem((void* )quoted_identifier); + return quoted_identifier2; } -(NSString* )quoteString:(NSString* )string { - if(_connection==nil) { - return nil; - } - const char* quoted_string = PQescapeLiteral(_connection,[string UTF8String],[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); - if(quoted_string==nil) { - return nil; - } - NSString* quoted_string2 = [NSString stringWithUTF8String:quoted_string]; - NSParameterAssert(quoted_string2); - PQfreemem((void* )quoted_string); - return quoted_string2; + if(_connection==nil) { + return nil; + } + const char* quoted_string = PQescapeLiteral(_connection,[string UTF8String],[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + if(quoted_string==nil) { + return nil; + } + NSString* quoted_string2 = [NSString stringWithUTF8String:quoted_string]; + NSParameterAssert(quoted_string2); + PQfreemem((void* )quoted_string); + return quoted_string2; } -(NSString* )encryptedPassword:(NSString* )password role:(NSString* )roleName { - NSParameterAssert(password); - NSParameterAssert(roleName); - if(_connection==nil) { - return nil; - } - char* encryptedPassword = PQencryptPassword([password UTF8String],[roleName UTF8String]); - if(encryptedPassword==nil) { - return nil; - } - NSString* encryptedPassword2 = [NSString stringWithUTF8String:encryptedPassword]; - NSParameterAssert(encryptedPassword2); - PQfreemem(encryptedPassword); - return encryptedPassword2; + NSParameterAssert(password); + NSParameterAssert(roleName); + if(_connection==nil) { + return nil; + } + char* encryptedPassword = PQencryptPassword([password UTF8String],[roleName UTF8String]); + if(encryptedPassword==nil) { + return nil; + } + NSString* encryptedPassword2 = [NSString stringWithUTF8String:encryptedPassword]; + NSParameterAssert(encryptedPassword2); + PQfreemem(encryptedPassword); + return encryptedPassword2; +} +-(id)copyWithZone:(NSZone *)zone +{ + + PGConnection* newClass = [[[self class] allocWithZone:zone] init]; + (newClass)->_connection = (self)->_connection; + (newClass)->_cancel = (self)->_cancel; + // (newClass)->_callback = (self)->_callback; + (newClass)->_socket = (self)->_socket; + (newClass)->_runloopsource = (self)->_runloopsource ; + (newClass)->_timeout = (self)->_timeout; + (newClass)->_state = (self)->_state; + + + (newClass)->_parameters = (self)->_parameters; + (newClass)->_tupleFormat = (self)->_tupleFormat; + return newClass; + } - @end diff --git a/src/Frameworks/PGClientKit/PGConnectionOperation.h b/src/Frameworks/PGClientKit/PGConnectionOperation.h new file mode 100644 index 0000000..aa727bb --- /dev/null +++ b/src/Frameworks/PGClientKit/PGConnectionOperation.h @@ -0,0 +1,62 @@ +// ************************************* +// +// Copyright 2017 - ?? Sebastien Cotillard - Genose.org +// https://github.com/genose +// +// ************************************* +// +// Copyright 2009-2015 David Thorpe +// https://github.com/djthorpe/postgresql-kit +// +// Originaly Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +#import + + +@class PGConnection; +@interface PGConnectionOperation : NSObject +{ + + void * _operationConnectionRef; + id _operationConnectionClassRef; + + void * _callbackWhenDone; + void * _callbackWhenError; + id _operation; + id _operationInfo; + id _operationType; + NSInteger _poolRefIdentifier; + NSInteger _operationStatus; + bool _invalidated; + dispatch_semaphore_t semaphore; + id resultsSet; +} +//(void(^)(id result,NSError* error)) +-(instancetype)initWithParametersDelegate:(id)connectionDelegate withRefPoolIdentifier:(NSInteger)poolIdentifier refClassOperation:(id)operation callWhenDone:(void*) callBackBlockDone callWhenError:(void(^)(id result,NSError* error)) callBackBlockError; + +-(PGConnection*)getConnectionDelegate; + +-(dispatch_semaphore_t)semaphore; +-(NSInteger)poolIdentifier; + +-(id)queryString; +-(id)UTF8String; + +-(void)finish; +-(bool)valid; +-(void)validate; +-(void)invalidate; +-(void *)getCallback; + +-(id)setResults:(id)results; +-(id)results; + +@end diff --git a/src/Frameworks/PGClientKit/PGConnectionOperation.m b/src/Frameworks/PGClientKit/PGConnectionOperation.m new file mode 100644 index 0000000..5416d3f --- /dev/null +++ b/src/Frameworks/PGClientKit/PGConnectionOperation.m @@ -0,0 +1,202 @@ +// ************************************* +// +// Copyright 2017 - ?? Sebastien Cotillard - Genose.org +// 07/2017 Sebastien Cotillard +// https://github.com/genose +// +// ************************************* +// +// Copyright 2009-2015 David Thorpe +// https://github.com/djthorpe/postgresql-kit +// +// Originaly Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + + +#import "PGConnectionOperation.h" +#import + +@implementation PGConnectionOperation + +-(instancetype)init +{ + self = [super init]; + if( self == nil) return nil; + + + _operation = [NSObject new]; + _operationInfo = nil; + _callbackWhenDone = NULL; + _callbackWhenError = NULL; + _invalidated = YES; + return self; + +} +-(instancetype)initWithParametersDelegate:(id)connectionDelegate withRefPoolIdentifier:(NSInteger)poolIdentifier refClassOperation:(id)operation callWhenDone:(void*) callBackBlockDone callWhenError:(void*) callBackBlockError +{ + // void (^callback)(PGResult* result,NSError* error) = (__bridge void (^)(PGResult* ,NSError* ))(_callback); + self = [[[self class] alloc ] init]; + if(self != nil) + { + _operationConnectionClassRef = connectionDelegate; +// _operationConnectionRef = ((PGConnection*)_operationConnectionClassRef)->_connection; + + + _operationType = [((NSObject*)operation) class]; + _operation = operation; + + + _poolRefIdentifier = poolIdentifier; + + _callbackWhenDone = (__bridge_retained void* )([(__bridge void (^)(void* ,void* ))(callBackBlockDone) copy]);//(__bridge void (^)(PGResult* ,NSError* ))(callBackBlockDone); + _callbackWhenError = (__bridge_retained void* )([(__bridge void (^)(void* ,void* ))(callBackBlockError) copy]);//(__bridge void (^)(PGResult* ,NSError* ))(callBackBlockDone); + _invalidated = NO; + + semaphore = dispatch_semaphore_create(0); + + }else{ + return nil; + } + return self; +} +-(bool)valid +{ + bool ret_valid = (_callbackWhenDone == nil) ? false : !_invalidated ; + return ret_valid; +} +-(void)finish +{ + _callbackWhenDone = nil; +} +-(void)invalidate +{ +#if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" %@::%@ :: INVALIDATE pool (%d :: %@ ) .... ", NSStringFromClass([self class]), NSStringFromSelector(_cmd), _poolRefIdentifier, [self description]); +#endif + if(_poolRefIdentifier !=0){ + [_operationConnectionClassRef invalidateOperation: _poolRefIdentifier]; + [self finish]; + } + + + _invalidated = TRUE; +} + +-(void)validate +{ + #if defined(DEBUG) && defined(DEBUG2) && DEBUG == 1 && DEBUG2 == 1 + NSLog(@" %@::%@ :: REACTIVATE pool (%d :: %@ ) .... ", NSStringFromClass([self class]), NSStringFromSelector(_cmd), _poolRefIdentifier, [self description]); +#endif + if(_poolRefIdentifier !=0) + [_operationConnectionClassRef invalidateOperation: _poolRefIdentifier]; + _invalidated = NO; +} +-(PGConnection*)getConnectionDelegate +{ + return _operationConnectionClassRef; + +} +-(NSInteger)poolIdentifier +{ + return _poolRefIdentifier; + +} +-(dispatch_semaphore_t)semaphore +{ + return semaphore; +} +-(void *)getCallback +{ +// if(invalidated){ +// _callbackWhenDone = nil; +// }; + return _callbackWhenDone; +} +#pragma mark results +-(id)setResults:(id)results +{ +// if(results && [results respondsToSelector:@selector(copyWithZone:)]) +// { +// resultsSet = [results copy]; +// }else +// { + resultsSet = results; +// } + + return resultsSet; +} +-(id)results +{ + return resultsSet; +} + +#pragma mark String Readable +-(id)UTF8String +{ + if([_operation respondsToSelector:@selector(UTF8String)]) + { + return [_operation UTF8String]; + } + + return nil; +} + +-(id)queryString +{ + if([_operation respondsToSelector:@selector(queryString)]) + { + return [_operation queryString]; + } + + return nil; +} + +-(id)string +{ + + @try{ + + + id queryStringDescription = [self queryString]; + if(queryStringDescription != nil) + { + return queryStringDescription; + } + + if([_operation isKindOfClass:[NSString class]] && [_operation respondsToSelector:@selector(UTF8String)]) + { + return [NSString stringWithFormat:@"<%@> : %@ ",NSStringFromClass([_operation class]), _operation ]; + } + + if([_operation respondsToSelector:@selector(string)]) + { + return [_operation string]; + } + + }@catch (NSException *exception) { + NSLog(@" %@ exeception .... %@",NSStringFromSelector(_cmd),exception); + } + @finally{} + + return @""; +} +-(NSString *)description +{ + @try + { + return [NSString stringWithFormat:@"<%@:%p> \n OperationType : %@ \n Connection Delegate : <%@:%p> \n Operation (%@)",NSStringFromClass([self class]), self, _operationType, NSStringFromClass([_operationConnectionClassRef class ]), _operationConnectionClassRef, [self string] ]; + +}@catch (NSException *exception) { + NSLog(@" %@ exeception .... %@",NSStringFromSelector(_cmd),exception); +} +@finally{} + return @""; +} +@end diff --git a/src/Frameworks/PGClientKit/PGPasswordStore.m b/src/Frameworks/PGClientKit/PGPasswordStore.m index 5ceade7..d168af8 100644 --- a/src/Frameworks/PGClientKit/PGPasswordStore.m +++ b/src/Frameworks/PGClientKit/PGPasswordStore.m @@ -60,7 +60,13 @@ -(NSString* )_accountForURL:(NSURL* )url { value = [NSString stringWithFormat:@"%lu",(unsigned long)PGClientDefaultPort]; } if(value) { - [parts addObject:[NSString stringWithFormat:@"%@=%@",key,[[value description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; +#if ( defined(__IPHONE_10_3) && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3 ) || ( defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_12 ) + NSCharacterSet *allowedCharset = [NSCharacterSet URLPathAllowedCharacterSet]; + + [parts addObject:[NSString stringWithFormat:@"%@=%@",key,[[value description] stringByAddingPercentEncodingWithAllowedCharacters: allowedCharset ]]]; +#else + [parts addObject:[NSString stringWithFormat:@"%@=%@",key,[[value description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; +#endif } } return [parts componentsJoinedByString:@";"]; diff --git a/src/Frameworks/PGClientKit/PGQueryObject.h b/src/Frameworks/PGClientKit/PGQueryObject.h index 17d1953..ec1184c 100644 --- a/src/Frameworks/PGClientKit/PGQueryObject.h +++ b/src/Frameworks/PGClientKit/PGQueryObject.h @@ -45,6 +45,7 @@ * Return option flags */ @property NSUInteger options; +@property void* _callback; //////////////////////////////////////////////////////////////////////////////// // methods @@ -102,4 +103,7 @@ */ -(NSString* )quoteForConnection:(PGConnection* )connection error:(NSError** )error; +-(const char* )UTF8String; +-(NSString* )queryString; + @end diff --git a/src/Frameworks/PGClientKit/PGQueryObject.m b/src/Frameworks/PGClientKit/PGQueryObject.m index b237c40..c9db784 100644 --- a/src/Frameworks/PGClientKit/PGQueryObject.m +++ b/src/Frameworks/PGClientKit/PGQueryObject.m @@ -72,7 +72,7 @@ +(instancetype)queryWithDictionary:(NSDictionary* )dictionary class:(NSString* ) //////////////////////////////////////////////////////////////////////////////// #pragma mark properties //////////////////////////////////////////////////////////////////////////////// - +@synthesize _callback = _callback; @synthesize dictionary = _dictionary; @dynamic options; @@ -132,6 +132,22 @@ -(void)clearOptionFlags:(NSUInteger)flag { [self setOptions:([self options] & ~flag)]; } + +-(const char* )UTF8String +{ + return [[self queryString] UTF8String]; +} +-(NSString* )string +{ + return [self queryString]; +} +-(NSString* )queryString +{ + NSString* queryTales = [self objectForKey:PGQueryStatementKey]; + NSLog(@"%@(%p) :: %@ :: %@", NSStringFromClass([self class]), self, NSStringFromSelector(_cmd), queryTales ); + return queryTales; +} + @end diff --git a/src/Frameworks/PGClientKit/PGQueryUpdate.m b/src/Frameworks/PGClientKit/PGQueryUpdate.m index bf3c27c..426d8c4 100644 --- a/src/Frameworks/PGClientKit/PGQueryUpdate.m +++ b/src/Frameworks/PGClientKit/PGQueryUpdate.m @@ -16,5 +16,8 @@ #import @implementation PGQueryUpdate ++(PGQueryUpdate* )into:(id)source values:(NSDictionary* )values where:(id)where +{ +} @end diff --git a/src/Frameworks/PGClientKit/PGResult.h b/src/Frameworks/PGClientKit/PGResult.h index 794394c..a1f6de3 100644 --- a/src/Frameworks/PGClientKit/PGResult.h +++ b/src/Frameworks/PGClientKit/PGResult.h @@ -47,4 +47,20 @@ */ -(NSArray* )arrayForColumn:(NSString* )columnName; + +/* Transverseable object as Array */ + +-(instancetype)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys; // :: unimplemented + + +-(NSArray *)allKeys; +-(NSEnumerator *)keyEnumerator; + +-(id _Nullable )valueForKeyPath:(NSString * _Nonnull)keyPath ; + +-(id _Nullable )valueForKey:(NSString *_Nonnull)key ; +-(id _Nullable )valueForUndefinedKey:(NSString * _Nonnull)key ; +-(BOOL)keyExists:(NSString * _Nonnull)keyPath; + +-(NSInteger)count; @end diff --git a/src/Frameworks/PGClientKit/PGResult.m b/src/Frameworks/PGClientKit/PGResult.m index d309437..d8705bf 100644 --- a/src/Frameworks/PGClientKit/PGResult.m +++ b/src/Frameworks/PGClientKit/PGResult.m @@ -1,16 +1,16 @@ -// Copyright 2009-2015 David Thorpe -// https://github.com/djthorpe/postgresql-kit -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. + // Copyright 2009-2015 David Thorpe + // https://github.com/djthorpe/postgresql-kit + // + // Licensed under the Apache License, Version 2.0 (the "License"); you may not + // use this file except in compliance with the License. You may obtain a copy + // of the License at http://www.apache.org/licenses/LICENSE-2.0 + // + // Unless required by applicable law or agreed to in writing, software + // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + // License for the specific language governing permissions and limitations + // under the License. #import #import @@ -19,171 +19,302 @@ @implementation PGResult @dynamic size, numberOfColumns, affectedRows, dataReturned, columnNames, rowNumber, format; -//////////////////////////////////////////////////////////////////////////////// -// initialization + //////////////////////////////////////////////////////////////////////////////// + // initialization -(id)init { - return nil; + return nil; } -(id)initWithResult:(PGresult* )theResult format:(PGClientTupleFormat)format encoding:(NSStringEncoding)encoding { - self = [super init]; - if(self) { - NSParameterAssert(theResult); - _result = theResult; - _format = format; - _encoding = encoding; - _cachedData = [NSMutableDictionary dictionaryWithCapacity:PQntuples(_result)]; - } - return self; + self = [super init]; + if(self) { + NSParameterAssert(theResult); + _result = theResult; + _format = format; + _encoding = encoding; + _cachedData = [NSMutableDictionary dictionaryWithCapacity:PQntuples(_result)]; + } + return self; } -(id)initWithResult:(PGresult* )theResult format:(PGClientTupleFormat)format { - return [self initWithResult:theResult format:format encoding:NSUTF8StringEncoding]; + return [self initWithResult:theResult format:format encoding:NSUTF8StringEncoding]; } -(void)dealloc { - [NSThread sleepForTimeInterval:1.0]; - [_cachedData removeAllObjects]; - PQclear((PGresult* )_result); + @synchronized(self) { + + + [NSThread sleepForTimeInterval:0.1]; + [_cachedData removeAllObjects]; +// _result = ((PGresult* )_result); + if( _result != NULL ){ + PQclear((PGresult* )_result); + } + _result = NULL; + [NSThread sleepForTimeInterval:0.1]; + } } -//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// -(PGClientTupleFormat)format { - return _format; + return _format; } -(NSUInteger)size { - NSUInteger _number = NSIntegerMax; - if(_number==NSIntegerMax) { - _number = PQntuples(_result); - } - return _number; + NSParameterAssert(_result); + NSUInteger _number = NSIntegerMax; + if(_number==NSIntegerMax) { + _number = PQntuples(_result); + } + return _number; } -(NSUInteger)numberOfColumns { - NSUInteger _number = NSIntegerMax; - if(_number==NSIntegerMax) { - _number = PQnfields(_result); - } - return _number; + NSParameterAssert(_result); + NSUInteger _number = NSIntegerMax; + if(_number==NSIntegerMax) { + _number = PQnfields(_result); + } + return _number; } -(NSUInteger)affectedRows { - NSUInteger _number = NSIntegerMax; - if(_number==NSIntegerMax) { - NSString* affectedRows = [NSString stringWithUTF8String:PQcmdTuples(_result)]; - _number = [affectedRows integerValue]; - } - return _number; + NSParameterAssert(_result); + NSUInteger _number = NSIntegerMax; + if(_number==NSIntegerMax) { + NSString* affectedRows = [NSString stringWithUTF8String:PQcmdTuples(_result)]; + _number = [affectedRows integerValue]; + } + return _number; } -(BOOL)dataReturned { - return PQresultStatus(_result)==PGRES_TUPLES_OK ? YES : NO; + return PQresultStatus(_result)==PGRES_TUPLES_OK ? YES : NO; } -(NSArray* )columnNames { - NSUInteger numberOfColumns = [self numberOfColumns]; - NSMutableArray* theColumns = [NSMutableArray arrayWithCapacity:numberOfColumns]; - for(NSUInteger i = 0; i < numberOfColumns; i++) { - [theColumns addObject:[NSString stringWithUTF8String:PQfname(_result,(int)i)]]; - } - return theColumns; + NSParameterAssert(_result); + NSUInteger numberOfColumns = [self numberOfColumns]; + NSMutableArray* theColumns = [NSMutableArray arrayWithCapacity:numberOfColumns]; + for(NSUInteger i = 0; i < numberOfColumns; i++) { + [theColumns addObject:[NSString stringWithUTF8String:PQfname(_result,(int)i)]]; + } + return theColumns; } -(void)setRowNumber:(NSUInteger)rowNumber { - NSParameterAssert(rowNumber < [self size]); - _rowNumber = rowNumber; + NSParameterAssert(rowNumber < [self size]); + _rowNumber = rowNumber; } -(NSUInteger)rowNumber { - return _rowNumber; + return _rowNumber; } -//////////////////////////////////////////////////////////////////////////////// -// private methods + //////////////////////////////////////////////////////////////////////////////// + // private methods -(NSObject* )_tupleForRow:(NSUInteger)r column:(NSUInteger)c { - // check for null - if(PQgetisnull(_result,(int)r,(int)c)) { - return [NSNull null]; - } - // get bytes, length - const void* bytes = PQgetvalue(_result,(int)r,(int)c); - NSUInteger size = PQgetlength(_result,(int)r,(int)c); - NSParameterAssert(bytes); -// NSParameterAssert(size); - - switch(_format) { - case PGClientTupleFormatText: - return pgdata_text2obj(PQftype(_result,(int)c),bytes,size,_encoding); - case PGClientTupleFormatBinary: - return pgdata_bin2obj(PQftype(_result,(int)c),bytes,size,_encoding); - default: - return nil; - } -} - -//////////////////////////////////////////////////////////////////////////////// - -// return the current row as an array of NSObject values + // check for null + if(PQgetisnull(_result,(int)r,(int)c)) { + return [NSNull null]; + } + // get bytes, length + const void* bytes = PQgetvalue(_result,(int)r,(int)c); + NSUInteger size = PQgetlength(_result,(int)r,(int)c); + NSParameterAssert(bytes); + // NSParameterAssert(size); + + switch(_format) { + case PGClientTupleFormatText: + return pgdata_text2obj(PQftype(_result,(int)c),bytes,size,_encoding); + case PGClientTupleFormatBinary: + return pgdata_bin2obj(PQftype(_result,(int)c),bytes,size,_encoding); + default: + return nil; + } +} + + //////////////////////////////////////////////////////////////////////////////// + + // return the current row as an array of NSObject values -(NSArray* )fetchRowAsArray { - if(_rowNumber >= [self size]) { - return nil; - } - NSNumber* key = [NSNumber numberWithUnsignedInteger:_rowNumber]; - NSMutableArray* theArray = [_cachedData objectForKey:key]; - if(theArray==nil) { - // create the array - NSUInteger numberOfColumns = [self numberOfColumns]; - theArray = [NSMutableArray arrayWithCapacity:numberOfColumns]; - // fill in the columns - for(NSUInteger i = 0; i < numberOfColumns; i++) { - id obj = [self _tupleForRow:_rowNumber column:i]; - NSParameterAssert(obj); - [theArray addObject:obj]; - } - // cache the array - [_cachedData setObject:theArray forKey:key]; - } - // increment to next row, return - _rowNumber++; - return theArray; + if(_rowNumber >= [self size]) { + return nil; + } + NSNumber* key = [NSNumber numberWithUnsignedInteger:_rowNumber]; + NSMutableArray* theArray = [_cachedData objectForKey:key]; + if(theArray==nil) { + // create the array + NSUInteger numberOfColumns = [self numberOfColumns]; + theArray = [NSMutableArray arrayWithCapacity:numberOfColumns]; + // fill in the columns + for(NSUInteger i = 0; i < numberOfColumns; i++) { + id obj = [self _tupleForRow:_rowNumber column:i]; + NSParameterAssert(obj); + [theArray addObject:obj]; + } + // cache the array + [_cachedData setObject:theArray forKey:key]; + } + // increment to next row, return + _rowNumber++; + return theArray; } -(NSDictionary* )fetchRowAsDictionary { - return [NSDictionary dictionaryWithObjects:[self fetchRowAsArray] forKeys:[self columnNames]]; + return [NSDictionary dictionaryWithObjects:[self fetchRowAsArray] forKeys:[self columnNames]]; } -(NSArray* )arrayForColumn:(NSString* )columnName { - NSParameterAssert(columnName); - NSInteger c = [[self columnNames] indexOfObject:columnName]; - if(c < 0 || c >= [self numberOfColumns]) { - return nil; - } - NSUInteger size = [self size]; - if(size==0) { - return @[ ]; - } - NSMutableArray* array = [NSMutableArray arrayWithCapacity:size]; - for(NSUInteger i = 0; i < size; i++) { - [array addObject:[self _tupleForRow:i column:c]]; - } - return array; -} - -//////////////////////////////////////////////////////////////////////////////// + NSParameterAssert(columnName); + NSInteger c = [[self columnNames] indexOfObject:columnName]; + if(c < 0 || c >= [self numberOfColumns]) { + return nil; + } + NSUInteger size = [self size]; + if(size==0) { + return @[ ]; + } + NSMutableArray* array = [NSMutableArray arrayWithCapacity:size]; + for(NSUInteger i = 0; i < size; i++) { + [array addObject:[self _tupleForRow:i column:c]]; + } + return array; +} + + +#pragma mark ********* KVC Protocol ******** + + +-(void)setValue:(id)value forUndefinedKey:(NSString *)key +{ + NSLog(@" :::: \n :: %@ :: %@ \n :: %@ \n :::::",NSStringFromSelector(_cmd), key, self ); + NSMutableDictionary * dictTmp = [((NSMutableDictionary*)_cachedData) mutableCopy]; + [((NSMutableDictionary*)dictTmp) setValue:value forKey: key]; + [((NSMutableDictionary*)_cachedData) setDictionary: dictTmp]; + +} +- (void)setObject:(id)anObject forKey:(NSString * _Nonnull)key +{ + NSLog(@" :::: \n :: %@ :: %@ \n :: %@ \n :::::",NSStringFromSelector(_cmd), key, self ); + NSMutableDictionary * dictTmp = [((NSMutableDictionary*)_cachedData) mutableCopy]; + [((NSMutableDictionary*)dictTmp) setValue:anObject forKey: key]; + [((NSMutableDictionary*)_cachedData) setDictionary: dictTmp]; +} + +-(BOOL)keyExists:(NSString*)keyPath +{ + return ((BOOL)[((NSMutableDictionary*)_cachedData) valueForKeyPath: keyPath]); +} + +-(id)valueForKeyPath:(NSString *)keyPath +{ + return [_cachedData valueForKeyPath:keyPath]; +} + +-(id)valueForKey:(NSString *)key +{ + NSLog(@" :::: \n :: %@ :: %@ \n :: %@ \n :::::",NSStringFromSelector(_cmd), key, self ); + id value = nil; + if ( ! [key containsString: @"resultDetail."]) { + value = [_cachedData objectForKey:key]; + + if( nil == value ) + { + + value = [_cachedData valueForKeyPath:[NSString stringWithFormat:@"resultDetail.%@",key]]; + + + } + } + + return value; +} + +-(id)valueForUndefinedKey:(NSString *)key +{ + NSLog(@" :::: \n :: %@ :: %@ \n :: %@ \n :::::",NSStringFromSelector(_cmd), key, self ); + id value = [((NSMutableDictionary*)_cachedData) valueForKey:key]; + return value; +} + +-(id)objectForKey:(id)key +{ + // :: + NSLog(@" :::: \n :: %@ :: %@ \n :: %@ \n :::::",NSStringFromSelector(_cmd), key, self ); + id value = nil; + + value = [((NSMutableDictionary*)_cachedData) objectForKey:key]; + + if( nil == value ) + { +// if(![key respondsToSelector:@selector(UTF8String)]) +// { +// key = [NSString stringWithFormat:@"%ld",key]; +// } + + value = [((NSMutableDictionary*)_cachedData) valueForKeyPath:[NSString stringWithFormat:@"%@",key]]; + } + + + return value; +} + +-(NSUInteger)count +{ + +// bool cnt = [ _cachedData isKindOfClass:[NSDictionary class]] && [self dataReturned]; +// if(!cnt) +// { +// return 0; +// } +// +// id keysC = [((NSMutableDictionary*)_cachedData) allKeys]; +// NSUInteger allkeysC = [keysC count]; + NSInteger cnt = ( _result ) ? (long) PQntuples(_result): 0; + return cnt; +} + +-(NSArray *)allKeys +{ + + id keysC = [((NSMutableDictionary*)_cachedData) allKeys]; + return keysC ; +} + +-(NSEnumerator *)keyEnumerator +{ + return (([_cachedData respondsToSelector: @selector(keyEnumerator) ]) ? [_cachedData keyEnumerator] : nil); +} +-(instancetype)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys +{ + [NSException raise:NSInvocationOperationCancelledException format:@"\n ************ \n ********** \n ERROR :: Unimplemented :: %@ :: \n >>> args / keys :: %@ \n >>>> keys :: %@ \n ************ \n ********** \n ",NSStringFromSelector(_cmd), keys, objects]; + return self; +} + +-(id)copyWithZone:(NSZone *)zone +{ + + PGResult* newClass = [[[self class] allocWithZone:zone] initWithResult: _result format: [self format] encoding: _encoding ]; + + return newClass; + +} + //////////////////////////////////////////////////////////////////////////////// -(NSString* )description { - NSDictionary* theValues = - [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithUnsignedInteger:[self size]],@"size", - [NSNumber numberWithUnsignedInteger:[self affectedRows]],@"affectedRows", - [NSNumber numberWithBool:[self dataReturned]],@"dataReturned", - [self columnNames],@"columnNames",nil]; - return [theValues description]; + NSDictionary* theValues = + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithUnsignedInteger:[self size]],@"size", + [NSNumber numberWithUnsignedInteger:[self affectedRows]],@"affectedRows", + [NSNumber numberWithBool:[self dataReturned]],@"dataReturned", + [self columnNames],@"columnNames",nil]; + return [theValues description]; } @end