// // $Id$ // // Databases & Tables.m // SPMySQLFramework // // Created by Rowan Beentje (rowan.beent.je) on February 11, 2012 // Copyright (c) 2012 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 "Databases & Tables.h" #import "SPMySQL Private APIs.h" @implementation SPMySQLConnection (Databases_and_Tables) #pragma mark - #pragma mark Database selection /** * Selects the database the connection should work with. Typically, a database should be * set on a connection before any database-specific queries are run. * Returns whether the database was correctly set or not. * As MySQL does not support deselecting databases, a nil databaseName will return NO. */ - (BOOL)selectDatabase:(NSString *)aDatabase { // If no database was supplied, can't deselected - return NO. if (!aDatabase) return NO; // Database selection should be made in UTF8 to avoid name encoding issues BOOL encodingChangeRequired = [self _storeAndAlterEncodingToUTF8IfRequired]; // Attempt to select the supplied database [self queryString:[NSString stringWithFormat:@"USE %@", [aDatabase mySQLBacktickQuotedString]]]; // If selecting the database failed, return failure. if ([self queryErrored]) { // If the encoding needs to be restored, the error message and ID have to be stored so the // actual error is still available to inspect on the class... if (encodingChangeRequired) { NSString *theErrorString = [self lastErrorMessage]; NSUInteger theErrorID = [self lastErrorID]; [self restoreStoredEncoding]; [self _updateLastErrorMessage:theErrorString]; [self _updateLastErrorID:theErrorID]; } return NO; } // Restore the connection encoding if necessary if (encodingChangeRequired) [self restoreStoredEncoding]; // Store new database name and return success if (database) [database release]; database = [[NSString alloc] initWithString:aDatabase]; return YES; } #pragma mark - #pragma mark Database lists /** * Retrieve an array of databases available to the current user, ordered as MySQL * returns them. * If an error occurred while retrieving the list of databases, nil will be returned; * if no databases are available, an empty array will be returned. */ - (NSArray *)databases { // Wrap the related databasesLike: function to avoid code duplication return [self databasesLike:nil]; } /** * Retrieve an array of databases whose names are matched against the supplied name * using MySQL LIKE syntax (with wildcard support for % and _). If no name is supplied, * all databases will be returned, in the order that MySQL returns them. * If an error occurred while retrieving the list of databases, nil will be returned; * if no matching databases are available, an empty array will be returned. */ - (NSArray *)databasesLike:(NSString *)nameLikeString { NSMutableArray *databaseList = nil; // Database display should be made in UTF8 to avoid name encoding issues BOOL encodingChangeRequired = [self _storeAndAlterEncodingToUTF8IfRequired]; // Build the query as appropriate NSMutableString *databaseQuery = [NSMutableString stringWithString:@"SHOW DATABASES"]; if ([nameLikeString length]) { [databaseQuery appendFormat:@" LIKE %@", [nameLikeString mySQLTickQuotedString]]; } // Perform the query and record state SPMySQLResult *databaseResult = [self queryString:databaseQuery]; [databaseResult setDefaultRowReturnType:SPMySQLResultRowAsArray]; // Retrieve the result into an array if the query was successful if (![self queryErrored]) { databaseList = [NSMutableArray arrayWithCapacity:(NSUInteger)[databaseResult numberOfRows]]; for (NSArray *dbRow in databaseResult) { [databaseList addObject:[dbRow objectAtIndex:0]]; } } // Restore the connection encoding if necessary if (encodingChangeRequired) [self restoreStoredEncoding]; return databaseList; } #pragma mark - #pragma mark Table lists /** * Retrieve an array of tables in the currently selected database, ordered as MySQL * returns them. * If an error occurred while retrieving the list of tables, nil will be returned; * if no tables are present, an empty array will be returned. */ - (NSArray *)tables { // Wrap the related tablesLike:fromDatabase: function to avoid code duplication return [self tablesLike:nil fromDatabase:nil]; } /** * Retrieve an array of tables in the currently selected database whose names are * matched against the supplied name using MySQL LIKE syntax (with wildcard * support for % and _). If no name is supplied, all tables in the selected * database will be returned, in the order that MySQL returns them. * If an error occurred while retrieving the list of tables, nil will be returned; * if no matching tables are present, an empty array will be returned. */ - (NSArray *)tablesLike:(NSString *)nameLikeString { // Wrap the related tablesLike:fromDatabase: function to avoid code duplication return [self tablesLike:nameLikeString fromDatabase:nil]; } /** * Retrieve an array of tables in the specified database, ordered as MySQL returns them. * If no database is specified, the current database will be used. * If an error occurred while retrieving the list of tables, nil will be returned; * if no tables are present in the specified database, an empty array will be returned. */ - (NSArray *)tablesFromDatabase:(NSString *)aDatabase { // Wrap the related tablesLike:fromDatabase: function to avoid code duplication return [self tablesLike:nil fromDatabase:aDatabase]; } /** * Retrieve an array of tables in the specified database whose names are matched * against the supplied name using MySQL LIKE syntax (with wildcard support * for % and _). If no name is supplied, all tables in the specified database * will be returned, in the order that MySQL returns them. * If no database is specified, the current database will be used. * If an error occurred while retrieving the list of tables, nil will be returned; * if no matching tables are present in the specified database, an empty array * will be returned. */ - (NSArray *)tablesLike:(NSString *)nameLikeString fromDatabase:(NSString *)aDatabase { NSMutableArray *tableList = nil; // Table display should be made in UTF8 to avoid name encoding issues BOOL encodingChangeRequired = [self _storeAndAlterEncodingToUTF8IfRequired]; // Build up the table lookup query NSMutableString *tableQuery = [NSMutableString stringWithString:@"SHOW TABLES"]; if ([aDatabase length]) { [tableQuery appendFormat:@" FROM %@", [aDatabase mySQLBacktickQuotedString]]; } if ([nameLikeString length]) { [tableQuery appendFormat:@" LIKE %@", [nameLikeString mySQLTickQuotedString]]; } // Perform the query and record state SPMySQLResult *tableResult = [self queryString:tableQuery]; [tableResult setDefaultRowReturnType:SPMySQLResultRowAsArray]; // Retrieve the result into an array if the query was successful if (![self queryErrored]) { tableList = [NSMutableArray arrayWithCapacity:(NSUInteger)[tableResult numberOfRows]]; for (NSArray *tableRow in tableResult) { [tableList addObject:[tableRow objectAtIndex:0]]; } } // Restore the connection encoding if necessary if (encodingChangeRequired) [self restoreStoredEncoding]; return tableList; } @end #pragma mark - #pragma mark Private API @implementation SPMySQLConnection (Databases_and_Tables_Private_API) /** * A number of queries regarding database or table information have to be made in UTF8, not * in the connection encoding, so that names can be fully displayed and used even if they * use a different encoding. This provides a convenience method to check whether a change * is required; if so, the current encoding is stored, the encoding is changed, and YES is * returned so the process can be reversed afterwards. */ - (BOOL)_storeAndAlterEncodingToUTF8IfRequired { // If the encoding is already UTF8, no change is required. if ([encoding isEqualToString:@"utf8"] && !encodingUsesLatin1Transport) return NO; // Store the current encoding for restoration afterwards, and update encoding [self storeEncodingForRestoration]; [self setEncoding:@"utf8"]; return YES; } @end