From b67728258595b7ce256ea50485ee6e6cb8137ac8 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 5 Mar 2014 03:36:32 +0100 Subject: Add support for SQLSTATE This commit adds the backend code to get the mysql SQLSTATE error code (to be used when displaying errors). --- .../SPMySQLFramework/Source/SPMySQL Private APIs.h | 1 + .../Databases & Tables.m | 2 ++ .../Querying & Preparation.h | 1 + .../Querying & Preparation.m | 37 ++++++++++++++++++++++ .../SPMySQLFramework/Source/SPMySQLConnection.h | 1 + .../SPMySQLFramework/Source/SPMySQLConnection.m | 4 +++ .../Source/SPMySQLFastStreamingResult.m | 1 + .../Source/SPMySQLStreamingResultStore.m | 1 + 8 files changed, 48 insertions(+) diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h index cb89ef4d..199d4790 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h +++ b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h @@ -84,6 +84,7 @@ - (void)_flushMultipleResultSets; - (void)_updateLastErrorMessage:(NSString *)theErrorMessage; - (void)_updateLastErrorID:(NSUInteger)theErrorID; +- (void)_updateLastSqlstate:(NSString *)theSqlstate; @end diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Databases & Tables.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Databases & Tables.m index dd84ff55..0b9651f6 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Databases & Tables.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Databases & Tables.m @@ -63,11 +63,13 @@ if (encodingChangeRequired) { NSString *theErrorString = [self lastErrorMessage]; NSUInteger theErrorID = [self lastErrorID]; + NSString *theSqlstate = [self lastSqlstate]; [self restoreStoredEncoding]; [self _updateLastErrorMessage:theErrorString]; [self _updateLastErrorID:theErrorID]; + [self _updateLastSqlstate:theSqlstate]; } return NO; diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.h index 4509fdc9..a595cfb2 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.h +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.h @@ -56,6 +56,7 @@ - (BOOL)queryErrored; - (NSString *)lastErrorMessage; - (NSUInteger)lastErrorID; +- (NSString *)lastSqlstate; + (BOOL)isErrorIDConnectionError:(NSUInteger)theErrorID; // Query cancellation diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m index 80cc798d..6fce5361 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m @@ -227,6 +227,7 @@ double queryExecutionTime; NSString *theErrorMessage; NSUInteger theErrorID; + NSString *theSqlstate; lastQueryWasCancelled = NO; lastQueryWasCancelledUsingReconnect = NO; @@ -299,6 +300,7 @@ if (!queryStatus) { theErrorMessage = nil; theErrorID = 0; + theSqlstate = nil; break; // If the query failed, determine whether to reattempt the query @@ -307,6 +309,7 @@ // Store the error state theErrorMessage = [self _stringForCString:mysql_error(mySQLConnection)]; theErrorID = mysql_errno(mySQLConnection); + theSqlstate = [self _stringForCString:mysql_sqlstate(mySQLConnection)]; // Prevent retries if the query was cancelled or not a connection error if (lastQueryWasCancelled || ![SPMySQLConnection isErrorIDConnectionError:mysql_errno(mySQLConnection)]) { @@ -319,6 +322,7 @@ if (![self checkConnection]) { [self _updateLastErrorMessage:theErrorMessage]; [self _updateLastErrorID:theErrorID]; + [self _updateLastSqlstate:theSqlstate]; return nil; } [self _lockConnection]; @@ -365,6 +369,7 @@ // Update the error message, if appropriate, to reflect result store errors or overall success theErrorMessage = [self _stringForCString:mysql_error(mySQLConnection)]; theErrorID = mysql_errno(mySQLConnection); + theSqlstate = [self _stringForCString:mysql_sqlstate(mySQLConnection)]; } else { theResult = [[SPMySQLEmptyResult alloc] init]; } @@ -379,6 +384,7 @@ if (lastQueryWasCancelled) { theErrorMessage = NSLocalizedString(@"Query cancelled.", @"Query cancelled error"); theErrorID = 1317; + theSqlstate = @"70100"; // If the query was cancelled on a MySQL <5 server, check the connection to allow reconnects // after query kills. This is also handled within the class for internal cancellations, but @@ -403,6 +409,7 @@ // Update error string and ID, and the rows affected [self _updateLastErrorMessage:theErrorMessage]; [self _updateLastErrorID:theErrorID]; + [self _updateLastSqlstate:theSqlstate]; lastQueryAffectedRowCount = theAffectedRowCount; // Store the result time on the response object @@ -474,6 +481,12 @@ return [NSString stringWithString:queryErrorMessage]; } +- (NSString *)lastSqlstate +{ + if(!querySqlstate) return nil; + return [NSString stringWithString:querySqlstate]; +} + /** * If the last query (or connection) triggered an error, returns the error * ID; if the last query did not error, 0 is returned. @@ -687,4 +700,28 @@ } } +/** + * Update the MySQL SQLSTATE for this connection. + * @param thSqlstate If a SQLSTATE is supplied it will be stored and returned to + * anything asking the instance for the last SQLSTATE; + * if nil is supplied, the connection will be used to derive + * (or clear) the SQLSTATE string; + * if @"" is supplied the SQLSTATE will only be cleared. + */ +- (void)_updateLastSqlstate:(NSString *)theSqlstate +{ + // If a SQLSTATE wasn't supplied, select one from the connection + if(!theSqlstate) { + theSqlstate = [self _stringForCString:mysql_sqlstate(mySQLConnection)]; + } + + // Clear the last SQLSTATE stored on the instance + if(querySqlstate) [querySqlstate release], querySqlstate = nil; + + // If we have a SQLSTATE *with a length*, update the instance SQLSTATE + if(theSqlstate && [theSqlstate length]) { + querySqlstate = [[NSString alloc] initWithString:theSqlstate]; + } +} + @end diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h index bf15d5b4..34b21043 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h @@ -104,6 +104,7 @@ // Error state for the last query or connection state NSUInteger queryErrorID; NSString *queryErrorMessage; + NSString *querySqlstate; // Query details unsigned long long lastQueryAffectedRowCount; diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m index cf08f749..79d5060a 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m @@ -165,6 +165,7 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS // Start with a blank error state queryErrorID = 0; queryErrorMessage = nil; + querySqlstate = nil; // Start with empty cancellation details lastQueryWasCancelled = NO; @@ -231,6 +232,7 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS if (databaseToRestore) [databaseToRestore release], databaseToRestore = nil; if (serverVersionString) [serverVersionString release], serverVersionString = nil; if (queryErrorMessage) [queryErrorMessage release], queryErrorMessage = nil; + if (querySqlstate) [querySqlstate release], querySqlstate = nil; [delegateDecisionLock release]; [NSObject cancelPreviousPerformRequestsWithTarget:self]; @@ -465,6 +467,7 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS // Clear the connection error record [self _updateLastErrorID:NSNotFound]; [self _updateLastErrorMessage:nil]; + [self _updateLastSqlstate:nil]; // Unlock the connection [self _unlockConnection]; @@ -566,6 +569,7 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS if (isMaster) { [self _updateLastErrorMessage:[self _stringForCString:mysql_error(theConnection)]]; [self _updateLastErrorID:mysql_errno(theConnection)]; + [self _updateLastSqlstate:[self _stringForCString:mysql_sqlstate(theConnection)]]; } return NULL; diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLFastStreamingResult.m b/Frameworks/SPMySQLFramework/Source/SPMySQLFastStreamingResult.m index 9b98a703..705b3daf 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLFastStreamingResult.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLFastStreamingResult.m @@ -393,6 +393,7 @@ typedef struct st_spmysqlstreamingrowdata { // Update the connection's error statuses to reflect any errors during the content download [parentConnection _updateLastErrorID:NSNotFound]; [parentConnection _updateLastErrorMessage:nil]; + [parentConnection _updateLastSqlstate:nil]; // Unlock the parent connection now all data has been retrieved [parentConnection _unlockConnection]; diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLStreamingResultStore.m b/Frameworks/SPMySQLFramework/Source/SPMySQLStreamingResultStore.m index eda82e87..447cf19b 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLStreamingResultStore.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLStreamingResultStore.m @@ -811,6 +811,7 @@ static inline void SPMySQLStreamingResultStoreFreeRowData(SPMySQLStreamingResult // Update the connection's error statuses to reflect any errors during the content download [parentConnection _updateLastErrorID:NSNotFound]; [parentConnection _updateLastErrorMessage:nil]; + [parentConnection _updateLastSqlstate:nil]; // Unlock the parent connection now all data has been retrieved [parentConnection _unlockConnection]; -- cgit v1.2.3