Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Utilisation de MySQL dans Cocoa

Compatible MacOS 10.6. Cliquer pour en savoir plus sur les compatibilités.Par uocram, le 06 juillet 2010
  • Révision : uocram - le 07/07/2010
  • Révision : uocram - le 08/07/2010
  • Révision : uocram - le 08/07/2010

Ce petit tutoriel vous fera découvrir les bases permettant de commencer à utiliser des données MySQL dans une application Cocoa. Application finie :
Application finie

Vous trouverez ici une discussion sur l'intérêt qu'il peut y avoir à utiliser MySQL plutôt que CoreData ou SQLite.
Lilive - le 13/08/2010
Prérequis
  • Mac Intel, Snow Leopard, avoir installé le package Developer (sur le disque d'installation de Snow Leopard)
  • Savoir utiliser (un peu) XCode, le langage C et Objective-C
  • Savoir utiliser Interface Builder
  • Savoir utiliser (un petit peu) le terminal

Installer MySQL

Télécharger MySQL

Télécharger le package MySQL

Installer le package MySQL

Avoir installé dans cet ordre (le nom des fichiers peut différer un peu selon la version) :

  1. mysql-5.5.0-m2-osx10.5-x86_64.pkg
  2. MySQL.prefPane
  3. MySQLStartupItem.pkg (puis redémarrez l'ordinateur)
  4. Dans les PréférencesMySQL activer le serveur au démarrage

Modifier le chemin du socket MySQL dans Snow Leopard

Copiez l'un des fichiers suivants au choix :
  • /usr/local/mysql/support-file/my-large.cnf
  • /usr/local/mysql/support-file/my-medium.cnf
  • /usr/local/mysql/support-file/my-small.cnf

dans /etc, sous le nom my.cnf

Puis modifiez le comme suit (dans un éditeur de texte brut) :

# The following options will be passed to all MySQL clients
[client]
socket	= /tmp/mysql.sock

et :

# The MySQL server 
[mysqld]
socket	= /tmp/mysql.sock
Pendant que vous avez les mains dans le camboui :

modifiez aussi le default_socket pour l'utilisation de php. Copier, dans /etc, le fichier php.ini.defaults, le coller dans /etc sous le nom php.ini puis le modifier comme suit :

; Default socket name for local MySQL connects. If empty, uses the built-in
; MySQL defaults.
mysql.default_socket = /tmp/mysql.sock

et aussi :

Default socket name for local MySQL connects. If empty, uses the built-in
; MySQL defaults.
mysqli.default_socket = /tmp/mysql.sock

Tester l'installation de MySQL dans le Terminal :

Connection à MySQL

Dans une fenêtre du Terminal

tapez :

cd /usr/local/mysql/bin

tapez Retour, puis :

./mysql -h localhost -u root -p

tapez Retour, puis donnez le mot de passe attendu : ”” (vide par défaut) donc taper Retour

Utilisation de MySQL

(juste pour un petit test : ce n'est pas le sujet du tutoriel)

Dans la fenêtre du Terminal

une fois connecté à MySQL, vous vous trouvez devant cette annonce de bienvenue :

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 7
Server version: 5.5.0-m2-log MySQL Community Server (GPL)

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

La dernière ligne est l'invite de commandes de mysql. Ne faisons pas attendre mysql et demandons lui la liste des bases sur ce serveur :

SHOW DATABASES;

N'oubliez pas le point virgule qui clôt la commande, sinon il attend la suite avant d'executer.

Voici le genre de résultat attendu (vous verrez surement moins de bases sur votre installation nouvelle : information_schema, mysql et test probablement) :

+--------------------+
| Database           |
+--------------------+
| information_schema |
| AdressesIP         |
| BaremeKilometrique |
| BaseIH             |
| BaseJM             |
| ChangementHeure    |
| Zorro              |
| mysql              |
| test               |
+--------------------+
9 rows in set (0.13 sec)

mysql> 

OK, ça fonctionne!

Pour quitter MySQL, tapez :

exit;

Passons à la suite, le sujet du tutoriel : utiliser MySQL dans une application cocoa.

Creer un projet dans XCode

Créer un nouveau projet dans XCode avec les caractéristiques suivantes :

Nouveau projet

Nommez le : CocoaMySQL

Paramétrage de CocoaMySQL pour utiliser MySQL

Ouvrir Targets dans Groups & Files colonne de gauche, et choisir Get Info :

Target -> Get Info

Pour la configuration All Configurations :

Paramétrer Other Linker Flags comme ci-dessous :

Other Linker Flags

Paramétrer Header Search Paths comme ci-dessous :

Header Search Paths

Paramétrer Library Search Paths comme ci-dessous :

Library Search Paths

Utilisation de MySQL dans l'application

Dans CocoaMySQLAppDelegate.h

Vous importez <mysql.h>
et vous déclarez MYSQL mysql;

//
//  CocoaMySQLAppDelegate.h
//  CocoaMySQL
//
//  Created by uocram on 06/07/10.
//
 
#import <Cocoa/Cocoa.h>
#import <mysql.h>
 
@interface CocoaMySQLAppDelegate : NSObject <NSApplicationDelegate> {
    NSWindow  *window;
    MYSQL     mysql;
}
 
@property (assign) IBOutlet NSWindow *window;
 
@end


Dans CocoaMySQLAppDelegate.m vous modifiez la methode applicationDidFinishLaunching: comme suit:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{ 
   const char *dbhost="localhost", *dbname="mysql", *dbpass="", *dbuser="root";
 
 
   mysql_init(&mysql); 
   mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, "2"); 
 
   if (!(mysql_real_connect(&mysql, dbhost, dbuser, dbpass, dbname, 0, NULL, 0))) 
   { 
	NSLog(@"\n\nErreur de connection!\nErreur :\n%s", mysql_error(&mysql)); 
   } 
   else 
   { 
	NSString	*a = NSLocalizedString(@"à", nil);
	NSString	*e = NSLocalizedString(@"é", nil);
	NSLog(@"\n\nConnection %@ la base \'%s\' r%@ussie!\n", a, dbname, e);
   } 
}


Vous faites un Build & Run et vous obtenez dans la Console :
Loading program into debugger…
Program loaded.
run
[Switching to process 2175]
Running…
2010-07-06 17:32:16.152 CocoaMySQL[2175:a0f] 

Connection à la base 'mysql' réussie!


Modifiez le nom de la base en choisissant un nom probablement inexistant comme theBaseWithNoName et vous obtenez l'erreur suivante lors de l'exécution :

Loading program into debugger…
Program loaded.
run
[Switching to process 2215]
Running…
2010-07-06 17:36:48.787 CocoaMySQL[2215:a0f] 

Erreur de connection!
Erreur :
Unknown database 'thebasewithnoname'

Conclusion

Et voilà!
Avec ça, vous êtes capable d'utiliser des bases de données MySQL dans une application Cocoa.
L'application, à ce niveau du tutoriel, est un peu frustre, mais c'est la graine qui va pousser de plus en plus vite, au fur et à mesure que vous allez creuser.

Où creuser

Principal lien incontournable : API MySQL C qui décrit tous les appels à la bibliothèque C de MySQL.

Un peu plus loin

Vous allez récuperer la liste des bases MySQL situées sur votre serveur et utiliser cette liste dans votre application.

Récuperer la liste des bases

Dans XCode

Vous ajoutez un NSArray, qui va contenir le nom des bases, dans CocoaMySQLAppDelegate.h :

#import <Cocoa/Cocoa.h>
#import <mysql.h>
 
@interface CocoaMySQLAppDelegate : NSObject <NSApplicationDelegate> {
        NSWindow	*window;
	MYSQL		mysql;
	NSArray		*dataBasesArray;
 
}
 
@property (assign) IBOutlet NSWindow			*window;
 
@end
Vous ajoutez cette methode :

- (NSArray *)DBList; dans CocoaMySQLAppDelegate.m methode qui renvoie un NSArray contenant la liste des bases sur ce serveur :

- (NSArray *)DBList
{
    MYSQL_RES *result;
    MYSQL_ROW row;
 
    unsigned int num_fields;
    int i;
 
    NSMutableArray *databases = [[NSMutableArray alloc] init];
 
    result = mysql_list_dbs(&mysql,NULL);
 
    if (result) {
        num_fields = mysql_num_fields(result);
 
        while (row = mysql_fetch_row(result))
        {
            unsigned long *lengths;
            lengths = mysql_fetch_lengths(result);
            for (i = 0; i < num_fields; i++)
            {
                NSString *dbName = [NSString stringWithFormat:@"%.*s ", (int) lengths[i], row[i] ? row[i] : "NULL"];
		dbName = [dbName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
                [databases insertObject:dbName atIndex:i];
            }
        }
 
        mysql_free_result(result);
 
	NSArray	*returnArray = [databases sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
 
	[databases release];
	return returnArray;
    }
 
    return nil;
}


Puis vous l'appelez dans la methode :

applicationDidFinishLaunching:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{
	const char *dbhost="localhost", *dbname="mysql", *dbpass="", *dbuser="root";
 
	mysql_init(&mysql); 
	mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, "2"); 
 
	if (!(mysql_real_connect(&mysql, dbhost, dbuser, dbpass, dbname, 0, NULL, 0))) 
	{ 
		NSLog(@"\n\nErreur de connection!\nErreur :\n%s", mysql_error(&mysql)); 
	} 
	else 
	{ 
		NSString	*a = NSLocalizedString(@"à", nil);
		NSString	*e = NSLocalizedString(@"é", nil);
		NSLog(@"\n\nConnection %@ la base \'%s\' r%@ussie!\n", a, dbname, e);
		// début de l'ajout
		dataBasesArray = [self DBList];
		NSLog(@"\n\nListe des bases :\n%@", dataBasesArray);
		// fin de l'ajout
	} 
}


Vous faites un Build & Run et vous obtenez dans la console :
Loading program into debugger…
Program loaded.
run
[Switching to process 2175]
Running…
2010-07-06 17:32:16.152 CocoaMySQL[2175:a0f] 

Connection à la base 'mysql' réussie!
2010-07-06 17:24:42.670 CocoaMySQL[2018:a0f] 

Liste des bases :
(
    AdressesIP,
    BaremeKilometrique,
    BaseIH,
    BaseJM,
    ChangementHeure,
    "information_schema",
    mysql,
    test,
    Zorro
)

votre affaire marche bien.

Vous avez 2 possibilités pour obtenir la liste des bases, contenue dans un jeu de résultats MYSQL_RES :
  1. en appelant la fonction de l'API C de MySQL :
    MYSQL_RES *result = mysql_list_dbs(MYSQL *mysql, const char *wild)
  2. Jeu de résultats que vous pouvez obtenir aussi en appelant mysql_real_query() comme suit :
    MYSQL_RES    *result;
    NSString     *query = @"SHOW databases";
     
    if (mysql_real_query(&mysql,[query cStringUsingEncoding:NSUTF8StringEncoding],[query length]) == 0)
    	result = mysql_store_result(&mysql);


Dans les deux cas, vous avez le même résultat que lors du test dans le terminal, lorsque vous avez envoyé la requête :

SHOW DATABASES;


Vous allez intégrer cette liste des bases dans un popup button, dans l'interface de votre application.

Dans XCode

dans CocoaMySQLAppDelegate.h, vous ajouter un IBOutlet NSPopUpButton :

#import <Cocoa/Cocoa.h>
#import <mysql.h>
 
@interface CocoaMySQLAppDelegate : NSObject <NSApplicationDelegate> {
        NSWindow	*window;
	MYSQL		mysql;
	NSArray		*dataBasesArray;
 
}
 
@property (assign) IBOutlet NSWindow			*window;
@property (assign) IBOutlet NSPopUpButton		*popup;
 
@end


Vous ajouter dans CocoaMySQLAppDelegate.m

@synthesize popup;


Dans Interface Builder

dans MainMenu.xib, vous ajoutez dans la fenêtre existante un NSPopUpButton pour la liste des bases que vous placez dans une NSToolbar et que vous connectez à l'outlet popup

Dans XCode

vous modifiez comme suit :

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{
	const char *dbhost="localhost", *dbname="mysql", *dbpass="", *dbuser="root";
 
	mysql_init(&mysql); 
	mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, "2"); 
 
	if (!(mysql_real_connect(&mysql, dbhost, dbuser, dbpass, dbname, 0, NULL, 0))) 
	{ 
		NSLog(@"\n\nErreur de connection!\nErreur :\n%s", mysql_error(&mysql)); 
	} 
	else 
	{ 
		NSString	*a = NSLocalizedString(@"à", nil);
		NSString	*e = NSLocalizedString(@"é", nil);
		NSLog(@"\n\nConnection %@ la base \'%s\' r%@ussie!\n", a, dbname, e);
		dataBasesArray = [self DBList];
		NSLog(@"\n\nListe des bases :\n%@", dataBasesArray);
		// début de l'ajout
		[popup removeAllItems];
		[popup addItemsWithTitles:dataBasesArray];
		[popup selectItemAtIndex:0];
		[popup setNeedsDisplay:YES];
		// fin de l'ajout
	} 
}


Cela donne :

Récuperer la liste des tables dans la base sélectionnée

Vous allez récupérer la liste des tables contenues dans la base sélectionnée dans le popup des bases et l'afficher dans une NSTableView.

Dans XCode

dans CocoaMySQLAppDelegate.h, vous ajouter un NSArray contenant les tables de la base sélectionnée ainsi que un IBOutlet tableView pour la NSTableView :

#import <Cocoa/Cocoa.h>
#import <mysql.h>
 
@interface CocoaMySQLAppDelegate : NSObject <NSApplicationDelegate> {
        NSWindow	*window;
	MYSQL		mysql;
	NSArray		*dataBasesArray;
	NSArray		*tablesArray;
 
}
 
@property (assign) IBOutlet NSWindow			*window;
@property (assign) IBOutlet NSPopUpButton		*popup;
@property (assign) IBOutlet NSTableView			*tableView;
 
@end


Vous ajouter dans CocoaMySQLAppDelegate.m

@synthesize tableView;


Dans Interface Builder

vous ajoutez dans MainMenu.xib une NSTableView pour la liste des tables contenues dans la base sélectionnée que vous connectez à l'outlet tableView : Vous connectez la NSTableView à CocoaMySQLAppDelegate comme dataSource.

Dans XCode

dans CocoaMySQLAppDelegate.m, vous allez implémenter 2 des 3 methodes de la dataSource :

  1. methode qui renvoie le nombre de rangées dans la table (en fait le nombre d'éléments du NSArray tablesArray) :
    - (int)numberOfRowsInTableView:(NSTableView *)tableView
    {
    	return [tablesArray count];
    }
  2. methode qui fournit la valeur pour la construction de chaque rangée de la table :
    - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn 
    			row:(NSInteger)rowIndex
    {
    	return [tablesArray objectAtIndex:rowIndex];
    }
  3. methode qui change la valeur de la rangée d'index rowIndex (non implémentée) :
    - (id)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject 
       forTableColumn:(NSTableColumn *)aTableColumn 
    			  row:(NSInteger)rowIndex
    {
    }


Toujours dans XCode

vous ajoutez les methodes qui mettent à jour la table, fonction de la base sélectionnée dans le popup :

  1. methode baseDidChange: appelée par le changement de base dans le popup et déclarée dans CocoaMySQLAppDelegate.h :
    #import <Cocoa/Cocoa.h>
    #import <mysql.h>
     
    @interface CocoaMySQLAppDelegate : NSObject <NSApplicationDelegate> {
        NSWindow	*window;
        MYSQL	mysql;
        NSArray	*dataBasesArray;
        NSArray	*tablesArray;
     
    }
     
    @property (assign) IBOutlet NSWindow			*window;
    @property (assign) IBOutlet NSPopUpButton		*popup;
    @property (assign) IBOutlet NSTableView			*tableView;
     
    - (IBAction)baseDidChange:(id)sender;
     
    @end

    Implémentée dans CocoaMySQLAppDelegate.m :

    - (IBAction)baseDidChange:(id)sender
    {
    	[self updateTablesForDB:[basesPopup titleOfSelectedItem]];
    }

    popup connecté à CocoaMySQLAppDelegate pour le selector baseDidChange: dans Interface Builder

  2. methode updateTablesForDB: effectuant la mise à jour fonction du nom de la base, implémentée dans CocoaMySQLAppDelegate.m :
    - (void)updateTablesForDB:(NSString *)baseName
    {
    	tablesArray = [[self tablesForDB:baseName] retain];
    	NSLog(@"\ntables : \n%@", tablesArray);
    	[tableView reloadData];
    }


  3. methode tablesForDB: renvoyant la liste des tables pour une base donnée :
    - (NSArray *)tablesForDB:(NSString *)dbName
    {
     	if (0 != mysql_select_db(&mysql,[dbName cStringUsingEncoding:NSUTF8StringEncoding]))
        {
    	NSLog(@"%s : Impossible de selectionner la base : %@", _cmd, dbName);
            return nil;
        }
    	return [self tables];
    }
  4. methode tables renvoyant, dans un NSArray, la liste des tables une fois connecté à une base :
    - (NSArray *)tables
    {
        MYSQL_RES *result;
        MYSQL_ROW row;
     
        unsigned int num_fields;
        int i;
     
        NSMutableArray *tables = [[NSMutableArray alloc] init];
     
        result = mysql_list_tables(&mysql,NULL);
     
        if (!result)
        {
            return nil;
        }
        else
        {
            num_fields = mysql_num_fields(result);
     
            while (row = mysql_fetch_row(result))
            {
                unsigned long *lengths;
                lengths = mysql_fetch_lengths(result);
                for (i=0; i < num_fields; i++)
                {
                    NSString *tableName = [NSString stringWithFormat:@"%.*s ", (int) lengths[i], row[i] ? row[i] : "NULL"];
    		tableName = [tableName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
                    [tables insertObject:tableName atIndex:i];
                }
            }
     
            mysql_free_result(result);
     
    	NSArray	*returnArray = [tables sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
            [tables autorelease];
    	return returnArray;
        }
    }


Cela donne :

Afficher la structure de la table sélectionnée

Dans XCode

dans CocoaMySQLAppDelegate.h, ajoutez une IBAction updateTableDescription: et un IBOutlet textField pour un NSTextField qui afficheront la description de la structure de la table sélectionnée :

#import <Cocoa/Cocoa.h>
#import <mysql.h>
 
@interface CocoaMySQLAppDelegate : NSObject <NSApplicationDelegate> {
    NSWindow	*window;
    MYSQL	mysql;
    NSArray	*dataBasesArray;
    NSArray	*tablesArray;
 
}
 
@property (assign) IBOutlet NSWindow			*window;
@property (assign) IBOutlet NSPopUpButton		*popup;
@property (assign) IBOutlet NSTableView			*tableView;
@property (assign) IBOutlet NSTextField			*textField;
 
- (IBAction)baseDidChange:(id)sender;
- (IBAction)updateTableDescription:(id)sender;
 
@end


Dans XCode, dans CocoaMySQLAppDelegate.m ajoutez :

@synthesize textField;
Dans Interface Builder

vous ajoutez un MultiLineTextField dans la fenêtre.
Un peu de cosmétique :

  • Police Menlo
  • size 11
  • fond noir
  • police couleur Printemps
  • Extensible avec la fenêtre

Vous le connectez à l'outlet textField du CocoaMySQLAppDelegate.

Vous ajoutez, dans la NSToolbar, un NSToolbarItem d'image name : NSInfo, de label et palette-label : Structure, que vous connectez au CocoaMySQLAppDelegate pour l'action updateTableDescription:

Interface

Dans XCode

vous ajoutez une nouvelle class MYSQLDescription.h et MYSQLDescription.m qui ne fait qu'une chose :
renvoyer une string de la description des colonnes de la table sélectionnée avec la methode - (NSString *)columnsDescription;
Cette string est d'un format proche de celui utilisé pour afficher les résultats des requêtes dans le terminal.

Toujours dans XCode

dans CocoaMySQLAppDelegate.m, vous modifiez comme suit :

#import "CocoaMySQLAppDelegate.h"
#import "MYSQLDescription.h"
 
@implementation CocoaMySQLAppDelegate
 
@synthesize window;
@synthesize popup;
@synthesize tableView;
@synthesize textField;
 
- (IBAction)updateTableDescription:(id)sender
{
	MYSQLDescription	*description;
	description = [[MYSQLDescription alloc] initWithTable:[tablesArray objectAtIndex:[tableView selectedRow]]
										    base:[popup titleOfSelectedItem] 
									  andMySQLHandle:mysql];
	NSLog(@"\nDescription de \'%@ -> %@\' :\n\n%@", 
             [popup titleOfSelectedItem], 
             [tablesArray objectAtIndex:[tableView selectedRow]], 
             [description columnsDescription]);
	[textField setStringValue:[description columnsDescription]];
}
 
- (NSArray *)DBList
{
    MYSQL_RES *result;
...


Les déclarations dans MYSQLDescription.h :
//
//  MYSQLDescription.h
//  CocoaMySQL
//
//  Created by uocram on 06/07/10.
//
 
#import <Cocoa/Cocoa.h>
#import <mysql.h>
 
@interface MYSQLDescription : NSObject {
 
	MYSQL			mysql;
 
	NSString		*table;
	NSString		*base;
 
	NSArray			*headerTitles;
	NSArray			*columnsLengths;
 
	NSStringEncoding	mEncoding;
 
}
 
@property (readwrite, retain) NSString *table;
@property (readwrite, retain) NSString *base;
 
- (id)initWithTable:(NSString *)aTable base:(NSString *)aBase andMySQLHandle:(MYSQL)aMysql;
 
- (NSString *)columnsDescription;
 
- (NSStringEncoding)encodingForMySQLEncoding:(const char *)mysqlEncoding;
 
@end
Et enfin, l'implémentation de MYSQLDescription.m :
//
//  MYSQLDescription.m
//  CocoaMySQL
//
//  Created by uocram on 06/07/10.
//
 
#import "MYSQLDescription.h"
 
@interface MYSQLDescription (private)
- (NSArray *)columnsLengthsArray;
- (NSArray *)headerTitlesArray;
@end
 
 
@implementation MYSQLDescription
 
@synthesize table;
@synthesize base;
 
- (id)initWithTable:(NSString *)aTable base:(NSString *)aBase andMySQLHandle:(MYSQL)aMysql
{
	if (self = [super init])
	{
		self.table = [aTable retain];
		self.base = [aBase retain];
		mysql = aMysql;
		mEncoding = [self encodingForMySQLEncoding:mysql_character_set_name(&mysql)];
		headerTitles = [[self headerTitlesArray] retain];
		columnsLengths = [[self columnsLengthsArray] retain];
 
	}
	return self;
}
 
- (NSArray *)columnsLengthsArray
{
	NSMutableArray	*lengthsArray = [NSMutableArray array];
	NSString	*query = [NSString stringWithFormat:@"SHOW COLUMNS FROM %@", self.table];
    if (mysql_real_query(&mysql,[query cStringUsingEncoding:mEncoding],[query length]) == 0)
    {
		MYSQL_RES		*result = mysql_store_result(&mysql);
		unsigned int	i, num_columns = mysql_num_fields(result);
		MYSQL_FIELD		*fields = mysql_fetch_fields(result);
		for (i = 0; i < num_columns;i++)
		{
			unsigned int	columnWidth = (fields[i].max_length > fields[i].name_length ? fields[i].max_length : fields[i].name_length);
			NSNumber		*length = [NSNumber numberWithUnsignedInt:columnWidth + 1];
			[lengthsArray addObject:length];
		}
	}
	return lengthsArray;
}
 
- (NSArray *)headerTitlesArray
{
	NSMutableArray	*title = [NSMutableArray array];
	NSString	*query = [NSString stringWithFormat:@"SHOW COLUMNS FROM %@", self.table];
    if (mysql_real_query(&mysql,[query cStringUsingEncoding:mEncoding],[query length]) == 0)
    {
		MYSQL_RES		*result = mysql_store_result(&mysql);
		unsigned int	i, num_columns = mysql_num_fields(result);
		MYSQL_FIELD		*fields = mysql_fetch_fields(result);
		for (i = 0; i < num_columns;i++)
		{
			NSString		*name = [NSString stringWithCString:fields[i].name encoding:mEncoding];
			[title addObject:name];
		}
	}
	return title;
}
 
- (NSString *)lineLikeTerminalWithLengths:(NSArray *)largeurs
{
	NSString	*resultString = @"";
	int		i , j;
	resultString = [resultString stringByAppendingString:@"+"];
	for (j = 0; j < [largeurs count]; j++)
	{
		for (i = 0; i <= [[largeurs objectAtIndex:j] unsignedIntValue]; i++)
			resultString = [resultString stringByAppendingString:@"-"];
		resultString = [resultString stringByAppendingString:@"+"];
	}
 
	return resultString;
}
 
- (NSString *)stringForRow:(MYSQL_ROW)row 
{
	NSString     *resultString = @"|";
	unsigned int num_fields = [columnsLengths count];
 
	// boucle sur chaque colonne
	int i;
	for (i = 0; i < num_fields; i++)
	{
		resultString = [resultString stringByAppendingFormat:@" %-*s|", [[columnsLengths objectAtIndex:i] unsignedIntValue], row[i] ? row[i] : " "];
	}
	resultString = [resultString stringByAppendingString:@"\n"];
 
	return resultString;
}
 
- (NSString *)rows
{
    NSString	*resultString = @"";
    NSString	*query = [NSString stringWithFormat:@"SHOW COLUMNS FROM %@", self.table];
 
    if (mysql_real_query(&mysql,[query cStringUsingEncoding:mEncoding],[query length]) == 0)
    {
		MYSQL_RES	*result = mysql_store_result(&mysql);
		MYSQL_ROW	row;
		while (row = mysql_fetch_row(result))
		{
			resultString = [resultString stringByAppendingString:[self stringForRow:row]];
		}
		// à la ligne
		resultString = [resultString stringByAppendingString:[self lineLikeTerminalWithLengths:columnsLengths]];
		resultString = [resultString stringByAppendingString:@"\n"];
	}		
 
	return resultString;
}
 
- (NSString *)headersTitlesLikeTerminal:(NSArray *)titles withLengths:(NSArray *)lengths
{
	NSString	*resultString = @"";
	int		j;
	resultString = [resultString stringByAppendingString:@"|"];
	for (j = 0; j < [lengths count]; j++)
	{
		resultString = [resultString stringByAppendingFormat:@" %-*s|", 
						[[lengths objectAtIndex:j] unsignedIntValue], 
						[[titles objectAtIndex:j] cStringUsingEncoding:mEncoding]
						];
	}
 
	return resultString;
}
 
- (NSString *)headers
{
	// 1ere ligne
	NSString	*resultString = [NSString stringWithFormat:@"mysql> describe %@;", self.table];
 
	resultString = [resultString stringByAppendingString:@"\n"];
	// cadre
	resultString = [resultString stringByAppendingString:[self lineLikeTerminalWithLengths:columnsLengths]];
	resultString = [resultString stringByAppendingString:@"\n"];
	// séparation
	resultString = [resultString stringByAppendingString:[self headersTitlesLikeTerminal:headerTitles withLengths:columnsLengths]];
	resultString = [resultString stringByAppendingString:@"\n"];
	// fin du cadre
	resultString = [resultString stringByAppendingString:[self lineLikeTerminalWithLengths:columnsLengths]];
	// à la ligne
	resultString = [resultString stringByAppendingString:@"\n"];
 
	return resultString;
}
 
- (NSString *)columnsDescription
{
	NSString	*resultString = @"";
 
	// Headers
	resultString = [resultString stringByAppendingString:[self headers]];
	// rows
	resultString = [resultString stringByAppendingString:[self rows]];
 
	return resultString;
}
 
- (NSStringEncoding)encodingForMySQLEncoding:(const char *)mysqlEncoding
{
	// Unicode encodings:
	if (!strncmp(mysqlEncoding, "utf8", 4)) {
		return NSUTF8StringEncoding;	// 4
	}
	if (!strncmp(mysqlEncoding, "ucs2", 4)) {
		return NSUnicodeStringEncoding;	// 10
	}
 
	// Roman alphabet encodings:
	if (!strncmp(mysqlEncoding, "ascii", 5)) {
		return NSASCIIStringEncoding;	// 1
	}
	if (!strncmp(mysqlEncoding, "latin1", 6)) {
		return NSISOLatin1StringEncoding;	// 5
	}
	if (!strncmp(mysqlEncoding, "macroman", 8)) {
		return NSMacOSRomanStringEncoding;	// 30
	}
 
	// Roman alphabet with central/east european additions:
	if (!strncmp(mysqlEncoding, "latin2", 6)) {
		return NSISOLatin2StringEncoding;	// 9
	}
	if (!strncmp(mysqlEncoding, "cp1250", 6)) {
		return NSWindowsCP1250StringEncoding;	// 15
	}
	if (!strncmp(mysqlEncoding, "win1250", 7)) {
		return NSWindowsCP1250StringEncoding;
	}
	if (!strncmp(mysqlEncoding, "cp1257", 6)) {
		return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingWindowsBalticRim);
	}
 
	// Additions for Turkish:
	if (!strncmp(mysqlEncoding, "latin5", 6)) {
		return NSWindowsCP1254StringEncoding;	// 14
	}
 
	// Greek:
	if (!strncmp(mysqlEncoding, "greek", 5)) {
		return NSWindowsCP1253StringEncoding;	// 13
	}
 
	// Cyrillic:
	if (!strncmp(mysqlEncoding, "win1251ukr", 6)) {
		return NSWindowsCP1251StringEncoding;	// 11
	}
	if (!strncmp(mysqlEncoding, "cp1251", 6)) {
		return NSWindowsCP1251StringEncoding;
	}
	if (!strncmp(mysqlEncoding, "koi8_ru", 6)) {
		return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingKOI8_R);
	}
	if (!strncmp(mysqlEncoding, "koi8_ukr", 6)) {
		return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingKOI8_R);
	}
 
	// Arabic:
	if (!strncmp(mysqlEncoding, "cp1256", 6)) {
		return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingWindowsArabic);
	}
 
	// Hebrew:
	if (!strncmp(mysqlEncoding, "hebrew", 6)) {
		CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatinHebrew);
	}
 
	// Asian:
	if (!strncmp(mysqlEncoding, "ujis", 4)) {
		return NSJapaneseEUCStringEncoding;	// 3
	}
	if (!strncmp(mysqlEncoding, "sjis", 4)) {
		return  NSShiftJISStringEncoding;	// 8
	}
	if (!strncmp(mysqlEncoding, "big5", 4)) {
		return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingBig5);
	}
	if (!strncmp(mysqlEncoding, "euc_kr", 6)) {
		return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingEUC_KR);
	}
	if (!strncmp(mysqlEncoding, "euckr", 5)) {
		return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingEUC_KR);
	}
 
	// Default to iso latin 1, even if it is not exact (throw an exception?)
	NSLog(@"WARNING: unknown name for MySQL encoding '%s'!\n\t\tFalling back to iso-latin1.", mysqlEncoding);
 
	return NSISOLatin1StringEncoding;
}
 
@end


Après un petit Build & Run cela donne :


Code source