From f4967d21057a1363cacc9607b5ace0149a45ca11 Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Mon, 11 Mar 2013 23:03:28 +0000 Subject: - Add a new SPMySQLEmptyResult class to SPMySQLFrameowkr, returning it instead of nil if a query produces no result set. This allows per-result-set properties to be preserved, fixing issues where information like query execution time was lost - addressing Issue #1577 --- Frameworks/SPMySQLFramework/SPMySQLEmptyResult.h | 35 +++++++ Frameworks/SPMySQLFramework/SPMySQLEmptyResult.m | 115 +++++++++++++++++++++ .../SPMySQLFramework.xcodeproj/project.pbxproj | 8 ++ Frameworks/SPMySQLFramework/Source/SPMySQL.h | 1 + .../Querying & Preparation.m | 56 +++++----- Frameworks/SPMySQLFramework/Source/SPMySQLResult.m | 39 ++++--- 6 files changed, 215 insertions(+), 39 deletions(-) create mode 100644 Frameworks/SPMySQLFramework/SPMySQLEmptyResult.h create mode 100644 Frameworks/SPMySQLFramework/SPMySQLEmptyResult.m (limited to 'Frameworks/SPMySQLFramework') diff --git a/Frameworks/SPMySQLFramework/SPMySQLEmptyResult.h b/Frameworks/SPMySQLFramework/SPMySQLEmptyResult.h new file mode 100644 index 00000000..153d5d68 --- /dev/null +++ b/Frameworks/SPMySQLFramework/SPMySQLEmptyResult.h @@ -0,0 +1,35 @@ +// +// $Id$ +// +// SPMySQLEmptyResult.h +// SPMySQLFramework +// +// Created by Rowan Beentje (rowan.beent.je) on March 11, 2013 +// Copyright (c) 2013 Rowan Beentje. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// More info at + +@interface SPMySQLEmptyResult : SPMySQLResult + +@end diff --git a/Frameworks/SPMySQLFramework/SPMySQLEmptyResult.m b/Frameworks/SPMySQLFramework/SPMySQLEmptyResult.m new file mode 100644 index 00000000..f8f7e268 --- /dev/null +++ b/Frameworks/SPMySQLFramework/SPMySQLEmptyResult.m @@ -0,0 +1,115 @@ +// +// $$ +// +// SPMySQLEmptyResult.m +// SPMySQLFramework +// +// Created by Rowan Beentje (rowan.beent.je) on March 11, 2013 +// Copyright (c) 2013 Rowan Beentje. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// More info at + +#import "SPMySQLEmptyResult.h" + +@implementation SPMySQLEmptyResult + +#pragma mark - +#pragma mark Setup and teardown + +/** + * Override the standard SPMySQLResult interface + */ +- (id)initWithMySQLResult:(void *)theResult stringEncoding:(NSStringEncoding)theStringEncoding +{ + return [super init]; +} + +- (void)dealloc +{ + [super dealloc]; +} + +#pragma mark - +#pragma mark Overrides + +- (NSUInteger)numberOfFields +{ + return 0; +} + +- (unsigned long long)numberOfRows +{ + return 0; +} + +- (NSArray *)fieldNames +{ + return nil; +} + +- (void)seekToRow:(unsigned long long)targetRow +{ +} + +- (id)getRow +{ + return nil; +} + +- (NSArray *)getRowAsArray +{ + return nil; +} + +- (NSDictionary *)getRowAsDictionary +{ + return nil; +} + +- (id)getRowAsType:(SPMySQLResultRowType)theType +{ + return nil; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len +{ + return 0; +} + +- (NSArray *)fieldDefinitions +{ + return nil; +} + +- (id)_stringWithBytes:(const void *)bytes length:(NSUInteger)length +{ + return nil; +} + +- (id)_getObjectFromBytes:(char *)bytes ofLength:(NSUInteger)length fieldType:(unsigned int)fieldType fieldDefinitionIndex:(NSUInteger)fieldIndex +{ + return nil; +} + +@end diff --git a/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj b/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj index c34ab9d2..1644f7d5 100644 --- a/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj +++ b/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj @@ -62,6 +62,8 @@ 58C7C1E514DB6E4C00436315 /* SPMySQLFastStreamingResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C7C1E314DB6E4C00436315 /* SPMySQLFastStreamingResult.m */; }; 58C7C1E814DB6E8600436315 /* Field Definitions.h in Headers */ = {isa = PBXBuildFile; fileRef = 58C7C1E614DB6E8600436315 /* Field Definitions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 58C7C1E914DB6E8600436315 /* Field Definitions.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C7C1E714DB6E8600436315 /* Field Definitions.m */; }; + 58D2A4D116EDF1C6002EB401 /* SPMySQLEmptyResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 58D2A4CF16EDF1C6002EB401 /* SPMySQLEmptyResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 58D2A4D216EDF1C6002EB401 /* SPMySQLEmptyResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 58D2A4D016EDF1C6002EB401 /* SPMySQLEmptyResult.m */; }; 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; }; 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; /* End PBXBuildFile section */ @@ -129,6 +131,8 @@ 58C7C1E314DB6E4C00436315 /* SPMySQLFastStreamingResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SPMySQLFastStreamingResult.m; path = Source/SPMySQLFastStreamingResult.m; sourceTree = ""; }; 58C7C1E614DB6E8600436315 /* Field Definitions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "Field Definitions.h"; path = "Source/SPMySQLResult Categories/Field Definitions.h"; sourceTree = ""; }; 58C7C1E714DB6E8600436315 /* Field Definitions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "Field Definitions.m"; path = "Source/SPMySQLResult Categories/Field Definitions.m"; sourceTree = ""; }; + 58D2A4CF16EDF1C6002EB401 /* SPMySQLEmptyResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPMySQLEmptyResult.h; sourceTree = ""; }; + 58D2A4D016EDF1C6002EB401 /* SPMySQLEmptyResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPMySQLEmptyResult.m; sourceTree = ""; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Resources/Info.plist; sourceTree = ""; }; 8DC2EF5B0486A6940098B216 /* SPMySQL.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SPMySQL.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; @@ -202,6 +206,8 @@ 584294EB14CB8002000F8438 /* Connection Categories */, 5884165314D2306A0078027F /* SPMySQLResult.h */, 5884165414D2306A0078027F /* SPMySQLResult.m */, + 58D2A4CF16EDF1C6002EB401 /* SPMySQLEmptyResult.h */, + 58D2A4D016EDF1C6002EB401 /* SPMySQLEmptyResult.m */, 586A99F914F02E21007F82BF /* SPMySQLStreamingResult.h */, 586A99FA14F02E21007F82BF /* SPMySQLStreamingResult.m */, 58C7C1E214DB6E4C00436315 /* SPMySQLFastStreamingResult.h */, @@ -397,6 +403,7 @@ 586AA81414F6C648007F82BF /* SPMySQLArrayAdditions.h in Headers */, 584D812E15057ECD00F24774 /* SPMySQLKeepAliveTimer.h in Headers */, 584D82551509775000F24774 /* Copying.h in Headers */, + 58D2A4D116EDF1C6002EB401 /* SPMySQLEmptyResult.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -485,6 +492,7 @@ 586AA16814F30C5F007F82BF /* Convenience Methods.m in Sources */, 584D812F15057ECD00F24774 /* SPMySQLKeepAliveTimer.m in Sources */, 584D82561509775000F24774 /* Copying.m in Sources */, + 58D2A4D216EDF1C6002EB401 /* SPMySQLEmptyResult.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQL.h b/Frameworks/SPMySQLFramework/Source/SPMySQL.h index aa9008b6..904f390c 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQL.h +++ b/Frameworks/SPMySQLFramework/Source/SPMySQL.h @@ -58,6 +58,7 @@ // MySQL result set, streaming subclasses of same, and associated categories #import "SPMySQLResult.h" +#import "SPMySQLEmptyResult.h" #import "SPMySQLStreamingResult.h" #import "SPMySQLFastStreamingResult.h" #import "Field Definitions.h" diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m index 22e35648..c007a07e 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m @@ -322,34 +322,38 @@ id theResult = nil; // On success, if there is a query result, retrieve the result data type - if (!queryStatus && mysql_field_count(mySQLConnection)) { - MYSQL_RES *mysqlResult; - - switch (theReturnType) { - - // For standard result sets, retrieve all the results now, and afterwards - // update the affected row count. - case SPMySQLResultAsResult: - mysqlResult = mysql_store_result(mySQLConnection); - theResult = [[SPMySQLResult alloc] initWithMySQLResult:mysqlResult stringEncoding:theEncoding]; - theAffectedRowCount = mysql_affected_rows(mySQLConnection); - break; - - // For fast streaming and low memory streaming result sets, set up the result - case SPMySQLResultAsLowMemStreamingResult: - mysqlResult = mysql_use_result(mySQLConnection); - theResult = [[SPMySQLStreamingResult alloc] initWithMySQLResult:mysqlResult stringEncoding:theEncoding connection:self]; - break; + if (!queryStatus) { + if (mysql_field_count(mySQLConnection)) { + MYSQL_RES *mysqlResult; + + switch (theReturnType) { + + // For standard result sets, retrieve all the results now, and afterwards + // update the affected row count. + case SPMySQLResultAsResult: + mysqlResult = mysql_store_result(mySQLConnection); + theResult = [[SPMySQLResult alloc] initWithMySQLResult:mysqlResult stringEncoding:theEncoding]; + theAffectedRowCount = mysql_affected_rows(mySQLConnection); + break; + + // For fast streaming and low memory streaming result sets, set up the result + case SPMySQLResultAsLowMemStreamingResult: + mysqlResult = mysql_use_result(mySQLConnection); + theResult = [[SPMySQLStreamingResult alloc] initWithMySQLResult:mysqlResult stringEncoding:theEncoding connection:self]; + break; + + case SPMySQLResultAsFastStreamingResult: + mysqlResult = mysql_use_result(mySQLConnection); + theResult = [[SPMySQLFastStreamingResult alloc] initWithMySQLResult:mysqlResult stringEncoding:theEncoding connection:self]; + break; + } - case SPMySQLResultAsFastStreamingResult: - mysqlResult = mysql_use_result(mySQLConnection); - theResult = [[SPMySQLFastStreamingResult alloc] initWithMySQLResult:mysqlResult stringEncoding:theEncoding connection:self]; - break; + // Update the error message, if appropriate, to reflect result store errors or overall success + theErrorMessage = [self _stringForCString:mysql_error(mySQLConnection)]; + theErrorID = mysql_errno(mySQLConnection); + } else { + theResult = [[SPMySQLEmptyResult alloc] init]; } - - // Update the error message, if appropriate, to reflect result store errors or overall success - theErrorMessage = [self _stringForCString:mysql_error(mySQLConnection)]; - theErrorID = mysql_errno(mySQLConnection); } // Update the connection's stored insert ID if available diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m b/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m index 3ccd5727..ee758bad 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m @@ -90,12 +90,27 @@ static id NSNullPointer; } /** - * Prevent SPMySQLResults from being init'd normally. + * Standard initialisation - not intended for external use. */ - (id)init { - [NSException raise:NSInternalInconsistencyException format:@"SPMySQLResults should not be init'd directly; use initWithMySQLResult:stringEncoding: instead."]; - return nil; + if ((self = [super init])) { + stringEncoding = NSASCIIStringEncoding; + queryExecutionTime = -1; + + resultSet = NULL; + numberOfFields = 0; + numberOfRows = 0; + currentRowIndex = 0; + + fieldDefinitions = NULL; + fieldNames = NULL; + fieldTypes = NULL; + + defaultRowReturnType = SPMySQLResultRowAsDictionary; + } + + return self; } /** @@ -108,15 +123,13 @@ static id NSNullPointer; // If no result set was passed in, return nil. if (!theResult) return nil; - if ((self = [super init])) { + if ((self = [self init])) { stringEncoding = theStringEncoding; - queryExecutionTime = -1; // Get the result set and cache the number of fields and number of rows resultSet = theResult; numberOfFields = mysql_num_fields(resultSet); numberOfRows = mysql_num_rows(resultSet); - currentRowIndex = 0; // Cache the field definitions and build up an array of cached field names and types fieldDefinitions = mysql_fetch_fields(resultSet); @@ -127,8 +140,6 @@ static id NSNullPointer; fieldNames[i] = [[self _stringWithBytes:aField.name length:aField.name_length] retain]; fieldTypes[i] = aField.type; } - - defaultRowReturnType = SPMySQLResultRowAsDictionary; } return self; @@ -136,13 +147,15 @@ static id NSNullPointer; - (void)dealloc { - mysql_free_result(resultSet); + if (resultSet) { + mysql_free_result(resultSet); - for (NSUInteger i = 0; i < numberOfFields; i++) { - [fieldNames[i] release]; + for (NSUInteger i = 0; i < numberOfFields; i++) { + [fieldNames[i] release]; + } + free(fieldNames); + free(fieldTypes); } - free(fieldNames); - free(fieldTypes); [super dealloc]; } -- cgit v1.2.3