//
//  $Id$
//
//  MCPResult.m
//  MCPKit
//
//  Created by Serge Cohen (serge.cohen@m4x.org) on 08/12/2002.
//  Copyright (c) 2001 Serge Cohen. All rights reserved.
//
//  Forked by the Sequel Pro team (sequelpro.com), April 2009
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  More info at <http://mysql-cocoa.sourceforge.net/>
//  More info at <http://code.google.com/p/sequel-pro/>

#import "MCPConnection.h"
#import "MCPNull.h"
#import "MCPNumber.h"
#import "MCPResult.h"

NSCalendarDate *MCPYear0000;

const OUR_CHARSET our_charsets60[] =
{
	{1,   "big5","big5_chinese_ci", 1, 2},
	{3,   "dec8", "dec8_swedisch_ci", 1, 1},
	{4,   "cp850", "cp850_general_ci", 1, 1},
	{6,   "hp8", "hp8_english_ci", 1, 1},
	{7,   "koi8r", "koi8r_general_ci", 1, 1},
	{8,   "latin1", "latin1_swedish_ci", 1, 1},
	{9,   "latin2", "latin2_general_ci", 1, 1},
	{10,  "swe7", "swe7_swedish_ci", 1, 1},
	{11,  "ascii", "ascii_general_ci", 1, 1},
	{12,  "ujis", "ujis_japanese_ci", 1, 3},
	{13,  "sjis", "sjis_japanese_ci", 1, 2},
	{16,  "hebrew", "hebrew_general_ci", 1, 1},
	{18,  "tis620", "tis620_thai_ci", 1, 1},
	{19,  "euckr", "euckr_korean_ci", 1, 2},
	{22,  "koi8u", "koi8u_general_ci", 1, 1},
	{24,  "gb2312", "gb2312_chinese_ci", 1, 2},
	{25,  "greek", "greek_general_ci", 1, 1},
	{26,  "cp1250", "cp1250_general_ci", 1, 1},
	{28,  "gbk", "gbk_chinese_ci", 1, 2},
	{30,  "latin5", "latin5_turkish_ci", 1, 1},
	{32,  "armscii8", "armscii8_general_ci", 1, 1},
	{33,  "utf8", "utf8_general_ci", 1, 3},
	{35,  "ucs2", "ucs2_general_ci", 2, 2},
	{36,  "cp866", "cp866_general_ci", 1, 1},
	{37,  "keybcs2", "keybcs2_general_ci", 1, 1},
	{38,  "macce", "macce_general_ci", 1, 1},
	{39,  "macroman", "macroman_general_ci", 1, 1},
	{40,  "cp852", "cp852_general_ci", 1, 1},
	{41,  "latin7", "latin7_general_ci", 1, 1},
	{51,  "cp1251", "cp1251_general_ci", 1, 1},
	{57,  "cp1256", "cp1256_general_ci", 1, 1},
	{59,  "cp1257", "cp1257_general_ci", 1, 1},
	{63,  "binary", "binary", 1, 1},
	{92,  "geostd8", "geostd8_general_ci", 1, 1},
	{95,  "cp932", "cp932_japanese_ci", 1, 2},
	{97,  "eucjpms", "eucjpms_japanese_ci", 1, 3},
	{2,   "latin2", "latin2_czech_cs", 1, 1},
	{5,   "latin1", "latin1_german_ci", 1, 1},
	{14,  "cp1251", "cp1251_bulgarian_ci", 1, 1},
	{15,  "latin1", "latin1_danish_ci", 1, 1},
	{17,  "filename", "filename", 1, 5},
	{20,  "latin7", "latin7_estonian_cs", 1, 1},
	{21,  "latin2", "latin2_hungarian_ci", 1, 1},
	{23,  "cp1251", "cp1251_ukrainian_ci", 1, 1},
	{27,  "latin2", "latin2_croatian_ci", 1, 1},
	{29,  "cp1257", "cp1257_lithunian_ci", 1, 1},
	{31,  "latin1", "latin1_german2_ci", 1, 1},
	{34,  "cp1250", "cp1250_czech_cs", 1, 1},
	{42,  "latin7", "latin7_general_cs", 1, 1},
	{43,  "macce", "macce_bin", 1, 1},
	{44,  "cp1250", "cp1250_croatian_ci", 1, 1},
	{45,  "utf8", "utf8_general_ci", 1, 1},
	{46,  "utf8", "utf8_bin", 1, 1},
	{47,  "latin1", "latin1_bin", 1, 1},
	{48,  "latin1", "latin1_general_ci", 1, 1},
	{49,  "latin1", "latin1_general_cs", 1, 1},
	{50,  "cp1251", "cp1251_bin", 1, 1},
	{52,  "cp1251", "cp1251_general_cs", 1, 1},
	{53,  "macroman", "macroman_bin", 1, 1},
	{58,  "cp1257", "cp1257_bin", 1, 1},
	{60,  "armascii8", "armascii8_bin", 1, 1},
	{65,  "ascii", "ascii_bin", 1, 1},
	{66,  "cp1250", "cp1250_bin", 1, 1},
	{67,  "cp1256", "cp1256_bin", 1, 1},
	{68,  "cp866", "cp866_bin", 1, 1},
	{69,  "dec8", "dec8_bin", 1, 1},
	{70,  "greek", "greek_bin", 1, 1},
	{71,  "hebew", "hebrew_bin", 1, 1},
	{72,  "hp8", "hp8_bin", 1, 1},
	{73,  "keybcs2", "keybcs2_bin", 1, 1},
	{74,  "koi8r", "koi8r_bin", 1, 1},
	{75,  "koi8u", "koi8u_bin", 1, 1},
	{77,  "latin2", "latin2_bin", 1, 1},
	{78,  "latin5", "latin5_bin", 1, 1},
	{79,  "latin7", "latin7_bin", 1, 1},
	{80,  "cp850", "cp850_bin", 1, 1},
	{81,  "cp852", "cp852_bin", 1, 1},
	{82,  "swe7", "swe7_bin", 1, 1},
	{93,  "geostd8", "geostd8_bin", 1, 1},
	{83,  "utf8", "utf8_bin", 1, 3},
	{84,  "big5", "big5_bin", 1, 2},
	{85,  "euckr", "euckr_bin", 1, 2},
	{86,  "gb2312", "gb2312_bin", 1, 2},
	{87,  "gbk", "gbk_bin", 1, 2},
	{88,  "sjis", "sjis_bin", 1, 2},
	{89,  "tis620", "tis620_bin", 1, 1},
	{90,  "ucs2", "ucs2_bin", 2, 2},
	{91,  "ujis", "ujis_bin", 1, 3},
	{94,  "latin1", "latin1_spanish_ci", 1, 1},
	{96,  "cp932", "cp932_bin", 1, 2},
	{99,  "cp1250", "cp1250_polish_ci", 1, 1},
	{98,  "eucjpms", "eucjpms_bin", 1, 3},
	{128, "ucs2", "ucs2_unicode_ci", 2, 2},
	{129, "ucs2", "ucs2_icelandic_ci", 2, 2},
	{130, "ucs2", "ucs2_latvian_ci", 2, 2},
	{131, "ucs2", "ucs2_romanian_ci", 2, 2},
	{132, "ucs2", "ucs2_slovenian_ci", 2, 2},
	{133, "ucs2", "ucs2_polish_ci", 2, 2},
	{134, "ucs2", "ucs2_estonian_ci", 2, 2},
	{135, "ucs2", "ucs2_spanish_ci", 2, 2},
	{136, "ucs2", "ucs2_swedish_ci", 2, 2},
	{137, "ucs2", "ucs2_turkish_ci", 2, 2},
	{138, "ucs2", "ucs2_czech_ci", 2, 2},
	{139, "ucs2", "ucs2_danish_ci", 2, 2},
	{140, "ucs2", "ucs2_lithunian_ci", 2, 2},
	{141, "ucs2", "ucs2_slovak_ci", 2, 2},
	{142, "ucs2", "ucs2_spanish2_ci", 2, 2},
	{143, "ucs2", "ucs2_roman_ci", 2, 2},
	{144, "ucs2", "ucs2_persian_ci", 2, 2},
	{145, "ucs2", "ucs2_esperanto_ci", 2, 2},
	{146, "ucs2", "ucs2_hungarian_ci", 2, 2},
	{147, "ucs2", "ucs2_sinhala_ci", 2, 2},
	{192, "utf8mb3", "utf8mb3_general_ci", 1, 3},
	{193, "utf8mb3", "utf8mb3_icelandic_ci", 1, 3},
	{194, "utf8mb3", "utf8mb3_latvian_ci", 1, 3},
	{195, "utf8mb3", "utf8mb3_romanian_ci", 1, 3},
	{196, "utf8mb3", "utf8mb3_slovenian_ci", 1, 3},
	{197, "utf8mb3", "utf8mb3_polish_ci", 1, 3},
	{198, "utf8mb3", "utf8mb3_estonian_ci", 1, 3},
	{119, "utf8mb3", "utf8mb3_spanish_ci", 1, 3},
	{200, "utf8mb3", "utf8mb3_swedish_ci", 1, 3},
	{201, "utf8mb3", "utf8mb3_turkish_ci", 1, 3},
	{202, "utf8mb3", "utf8mb3_czech_ci", 1, 3},
	{203, "utf8mb3", "utf8mb3_danish_ci", 1, 3},
	{204, "utf8mb3", "utf8mb3_lithunian_ci", 1, 3},
	{205, "utf8mb3", "utf8mb3_slovak_ci", 1, 3},
	{206, "utf8mb3", "utf8mb3_spanish2_ci", 1, 3},
	{207, "utf8mb3", "utf8mb3_roman_ci", 1, 3},
	{208, "utf8mb3", "utf8mb3_persian_ci", 1, 3},
	{209, "utf8mb3", "utf8mb3_esperanto_ci", 1, 3},
	{210, "utf8mb3", "utf8mb3_hungarian_ci", 1, 3},
	{211, "utf8mb3", "utf8mb3_sinhala_ci", 1, 3},
	{224, "utf8", "utf8_unicode_ci", 1, 3},
	{225, "utf8", "utf8_icelandic_ci", 1, 3},
	{226, "utf8", "utf8_latvian_ci", 1, 3},
	{227, "utf8", "utf8_romanian_ci", 1, 3},
	{228, "utf8", "utf8_slovenian_ci", 1, 3},
	{229, "utf8", "utf8_polish_ci", 1, 3},
	{230, "utf8", "utf8_estonian_ci", 1, 3},
	{231, "utf8", "utf8_spanish_ci", 1, 3},
	{232, "utf8", "utf8_swedish_ci", 1, 3},
	{233, "utf8", "utf8_turkish_ci", 1, 3},
	{234, "utf8", "utf8_czech_ci", 1, 3},
	{235, "utf8", "utf8_danish_ci", 1, 3},
	{236, "utf8", "utf8_lithuanian_ci", 1, 3},
	{237, "utf8", "utf8_slovak_ci", 1, 3},
	{238, "utf8", "utf8_spanish2_ci", 1, 3},
	{239, "utf8", "utf8_roman_ci", 1, 3},
	{240, "utf8", "utf8_persian_ci", 1, 3},
	{241, "utf8", "utf8_esperanto_ci", 1, 3},
	{242, "utf8", "utf8_hungarian_ci", 1, 3},
	{243, "utf8", "utf8_sinhala_ci", 1, 3},
	{254, "utf8mb3", "utf8mb3_general_cs", 1, 3},
	{0, NULL, NULL, 0, 0}
};

@implementation MCPResult

/**
 * Hold the results of a query to a MySQL database server. It correspond to the MYSQL_RES structure of the C API, and to the statement handle of the PERL DBI/DBD.
 *
 * Uses the !{mysql_store_result()} function from the C API. 
 *
 * This object is generated only by a MCPConnection object, in this way (see #{MCPConnection} documentation):
 *
 *	 MCPConnection *theConnec = [MCPConnection alloc];
 *	 MCPResult *theRes;
 *	 NSDictionary *theDict; 
 *	 NSArray *theColNames; 
 *	 int i, j; 
 *	 
 *	 theConnec = [theConnec initToHost:@"albert.com" withLogin:@"toto" password:@"albert" usingPort:0]; 
 *	 [theConnec selectDB:@"db1"];
 *	 theRes = [theConnec queryString:@"select * from table1"];
 *	 theColNames = [theRes fetchFiedlsName];
 *	 i = 0;
 *
 * 	 while (theDict = [theRes fetchRowAsDictionary]) {
 *		 NSLog(@"Row : %d\n", i); 
 *		 for (j=0; j<[theColNames count]; j++) {
 *			 NSLog(@"  Field : %@, contain : %@\n", [theColNames objectAtIndex:j], [theDict objectForKey:[theColNames objectAtIndex:j]]);
 * 		 }
 *		 i++; 
 *	 }
 */

/**
 * Initialize the class version to 3.0.1
 */
+ (void)initialize
{
	if (self = [MCPResult class]) {
		[self setVersion:030001]; // Ma.Mi.Re -> MaMiRe
		MCPYear0000 = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate:-63146822400.0];
		[MCPYear0000 setCalendarFormat:@"%Y"];
	}
}

#pragma mark -
#pragma mark Initialisation

/**
 * Empty init, normaly of NO use to the user, again, MCPResult should be made through calls to MCPConnection
 */
- (id)init
{
	if ((self = [super init])) {
		mEncoding = [MCPConnection defaultMySQLEncoding];
		
		if (mResult) {
			mysql_free_result(mResult);
			mResult = NULL;
		}
		
		if (mNames) {
			[mNames release];
			mNames = NULL;
		}

		mNumOfFields = 0;
	}
	
	return self;    
}

/**
 * Initialise a MCPResult, it is used internally by MCPConnection !{queryString:} method: the only proper 
 * way to get a running MCPResult object.
 */
- (id)initWithMySQLPtr:(MYSQL *)mySQLPtr encoding:(NSStringEncoding)iEncoding timeZone:(NSTimeZone *)iTimeZone
{
	if ((self = [super init])) {
		mEncoding = iEncoding;
		mTimeZone = [iTimeZone retain];
		
		if (mResult) {
			mysql_free_result(mResult);
			mResult = NULL;
		}
		
		if (mNames) {
			[mNames release];
			mNames = NULL;
		}
		
		mResult = mysql_store_result(mySQLPtr);
		
		if (mResult) {
			mNumOfFields = mysql_num_fields(mResult);
		}
		else {
			mNumOfFields = 0;
		}
	}
	
	return self;
}

/**
 * This metod is used internally by MCPConnection object when it have already a MYSQL_RES object to initialise 
 * MCPResult object. Initialise a MCPResult with the MYSQL_RES pointer (returned by such a function as mysql_list_dbs).
 * NB: MCPResult should be made by using one of the method of MCPConnection.
 */
- (id)initWithResPtr:(MYSQL_RES *)mySQLResPtr encoding:(NSStringEncoding)iEncoding timeZone:(NSTimeZone *)iTimeZone
{
	if ((self = [super init])) {
		mEncoding = iEncoding;
		mTimeZone = [iTimeZone retain];
		
		if (mResult) {
			mysql_free_result(mResult);
			mResult = NULL;
		}
		
		if (mNames) {
			[mNames release];
			mNames = NULL;
		}
		
		mResult = mySQLResPtr;
		
		if (mResult) {
			mNumOfFields = mysql_num_fields(mResult);
		}
		else {
			mNumOfFields = 0;
		}
	}
	
	return self;    
}

#pragma mark -
#pragma mark Result info

/**
 * Return the number of rows selected by the query.
 */
- (my_ulonglong)numOfRows
{
	if (mResult) {
		return mysql_num_rows(mResult);
	}
	
	return 0;
}

/**
 * Return the number of fields selected by the query. As a side effect it forces an update of the number of fields.
 */
- (NSUInteger)numOfFields
{
	if (mResult) {
		return mNumOfFields = mysql_num_fields(mResult);
	}
	
	return mNumOfFields = 0;
}

#pragma mark -
#pragma mark Rows

/**
 * Go to a precise row in the selected result. 0 is the very first row.
 */
- (void)dataSeek:(my_ulonglong)row
{
	my_ulonglong theRow = (row < 0)? 0 : row;
	theRow = (theRow < [self numOfRows]) ? theRow : ([self numOfRows] - 1);
	mysql_data_seek(mResult,theRow);
}

/**
 *
 */
- (id)fetchRowAsType:(MCPReturnType)aType
{
	MYSQL_ROW		theRow;
	unsigned long	*theLengths;
	MYSQL_FIELD		*theField;
	NSInteger				i;
	id				theReturn;
	
	if (mResult == NULL) {
		// If there is no results, returns nil, as after the last row...
		return nil;
	}
	
	theRow = mysql_fetch_row(mResult);
	
	if (theRow == NULL) {
		return nil;
	}
	
	switch (aType) {
		case MCPTypeArray:
			theReturn = [NSMutableArray arrayWithCapacity:mNumOfFields];
			break;
		case MCPTypeDictionary:
			if (mNames == nil) {
				[self fetchFieldNames];
			}
			theReturn = [NSMutableDictionary dictionaryWithCapacity:mNumOfFields];
			break;
		default :
			NSLog (@"Unknown type : %d, will return an Array!\n", aType);
			theReturn = [NSMutableArray arrayWithCapacity:mNumOfFields];
			break;
	}
	
	theLengths = mysql_fetch_lengths(mResult);
	theField = mysql_fetch_fields(mResult);
	
	for (i=0; i<mNumOfFields; i++) {
		id	theCurrentObj;
		
		if (theRow[i] == NULL) {
			theCurrentObj = [NSNull null];
		} else {
			char *theData = calloc(sizeof(char),theLengths[i]+1);
			//char *theUselLess;
			memcpy(theData, theRow[i],theLengths[i]);
			theData[theLengths[i]] = '\0';
			
			switch (theField[i].type) {
				case FIELD_TYPE_TINY:
				case FIELD_TYPE_SHORT:
				case FIELD_TYPE_INT24:
				case FIELD_TYPE_LONG:
				case FIELD_TYPE_LONGLONG:
				case FIELD_TYPE_DECIMAL:
				case FIELD_TYPE_NEWDECIMAL:
				case FIELD_TYPE_FLOAT:
				case FIELD_TYPE_DOUBLE:
				case FIELD_TYPE_TIMESTAMP:
				case FIELD_TYPE_DATE:
				case FIELD_TYPE_TIME:
				case FIELD_TYPE_DATETIME:
				case FIELD_TYPE_YEAR:
				case FIELD_TYPE_VAR_STRING:
				case FIELD_TYPE_STRING:
				case FIELD_TYPE_SET:
				case FIELD_TYPE_ENUM:
				case FIELD_TYPE_NEWDATE: // Don't know what the format for this type is...
					theCurrentObj = [NSString stringWithCString:theData encoding:mEncoding];
					break;
					
				case FIELD_TYPE_BIT:
					theCurrentObj = [NSString stringWithFormat:@"%u", theData[0]];
					break;
					
				case FIELD_TYPE_TINY_BLOB:
				case FIELD_TYPE_BLOB:
				case FIELD_TYPE_MEDIUM_BLOB:
				case FIELD_TYPE_LONG_BLOB:
					theCurrentObj = [NSData dataWithBytes:theData length:theLengths[i]];
					if (!(theField[i].flags & BINARY_FLAG)) { // It is TEXT and NOT BLOB...
						theCurrentObj = [self stringWithText:theCurrentObj];
					} // #warning Should check for TEXT (using theField[i].flag BINARY_FLAG)
					break;
					
				case FIELD_TYPE_NULL:
					theCurrentObj = [NSNull null];
					break;
					
				default:
					NSLog (@"in fetchRowAsType : Unknown type : %ld for column %ld, send back a NSData object", (NSInteger)theField[i].type, (NSInteger)i);
					theCurrentObj = [NSData dataWithBytes:theData length:theLengths[i]];
					break;
			}
			
			free(theData);
			
			// Some of the creators return nil object...
			if (theCurrentObj == nil) {
				theCurrentObj = [NSNull null];
			}
		}
		
		switch (aType) {
			case MCPTypeDictionary :
				[theReturn setObject:theCurrentObj forKey:[mNames objectAtIndex:i]];
				break;
				
			case MCPTypeArray :
			default :
				[theReturn addObject:theCurrentObj];
				break;
		}
	}
	
	return theReturn;
}

/**
 * Return the next row of the result as an array, the index in select field order, the object a proper object 
 * for handling the information in the field (NSString, NSNumber ...).
 *
 * Just a #{typed} wrapper for method !{fetchRosAsType:} (with arg MCPTypeArray).
 *
 * NB: Returned object is immutable.
 */
- (NSArray *)fetchRowAsArray
{
	NSMutableArray *theArray = [self fetchRowAsType:MCPTypeArray];
			
	return (theArray) ? [NSArray arrayWithArray:theArray] : nil;
}

/**
 * Return the next row of the result as a dictionary, the key being the field name, the object a proper object 
 * for handling the information in the field (NSString, NSNumber ...).
 *
 * Just a #{typed} wrapper for method !{fetchRosAsType:} (with arg MCPTypeDictionary).
 *
 * NB: Returned object is immutable.
 */
- (NSDictionary *)fetchRowAsDictionary
{
	NSMutableDictionary	*theDict = [self fetchRowAsType:MCPTypeDictionary];

	return (theDict) ? [NSDictionary dictionaryWithDictionary:theDict] : nil;
}

#pragma mark -
#pragma mark Columns

/**
 * Generate the mNames if not already generated, and return it.
 *
 * mNames is a NSArray holding the names of the fields(columns) of the results.
 */
- (NSArray *)fetchFieldNames
{
	NSInteger	i;
	NSUInteger theNumFields;
	NSMutableArray *theNamesArray;
	MYSQL_FIELD	*theField;
	
	if (mNames) {
		return mNames;
	}
	
	if (mResult == NULL) {
		// If no results, give an empty array. Maybe it's better to give a nil pointer?
		return (mNames = [[NSArray array] retain]);
	}
	
	theNumFields = [self numOfFields];
	theNamesArray = [NSMutableArray arrayWithCapacity: theNumFields];
	theField = mysql_fetch_fields(mResult);   
	
	for (i=0; i<theNumFields; i++) {
		NSString	*theName = [self stringWithCString:theField[i].name];
		if ((theName) && (![theName isEqualToString:@""])) {
			[theNamesArray addObject:theName];
		}
		else {
			[theNamesArray addObject:[NSString stringWithFormat:@"Column %ld", i]];
		}
	}
	
	return (mNames = [[NSArray arrayWithArray:theNamesArray] retain]);
}

/**
 * Return a collection of the fields's type. The type of collection is choosen by the aType variable 
 * (MCPTypeArray or MCPTypeDictionary).
 *
 * This method returned directly the #{mutable} object generated while going through all the columns
 */
- (id)fetchTypesAsType:(MCPReturnType)aType
{
	NSInteger i;
	id theTypes;
	MYSQL_FIELD	*theField;
	
	if (mResult == NULL) {
		// If no results, give an empty array. Maybe it's better to give a nil pointer?
		return nil;
	}
	
	switch (aType) {
		case MCPTypeArray:
			theTypes = [NSMutableArray arrayWithCapacity:mNumOfFields];
			break;
		case MCPTypeDictionary:
			if (mNames == nil) {
				[self fetchFieldNames];
			}
			theTypes = [NSMutableDictionary dictionaryWithCapacity:mNumOfFields];
			break;
		default :
			NSLog (@"Unknown type : %d, will return an Array!\n", aType);
			theTypes = [NSMutableArray arrayWithCapacity:mNumOfFields];
			break;
	}
	
	theField = mysql_fetch_fields(mResult);
	
	for (i=0; i<mNumOfFields; i++) {
		NSString	*theType;
		switch (theField[i].type) {
			case FIELD_TYPE_TINY:
				theType = @"tiny";
				break;
			case FIELD_TYPE_SHORT:
				theType = @"short";
				break;
			case FIELD_TYPE_LONG:
				theType = @"long";
				break;
			case FIELD_TYPE_INT24:
				theType = @"int24";
				break;
			case FIELD_TYPE_LONGLONG:
				theType = @"longlong";
				break;
			case FIELD_TYPE_DECIMAL:
			case FIELD_TYPE_NEWDECIMAL:
				theType = @"decimal";
				break;
			case FIELD_TYPE_FLOAT:
				theType = @"float";
				break;
			case FIELD_TYPE_DOUBLE:
				theType = @"double";
				break;
			case FIELD_TYPE_TIMESTAMP:
				theType = @"timestamp";
				break;
			case FIELD_TYPE_DATE:
				theType = @"date";
				break;
			case FIELD_TYPE_TIME:
				theType = @"time";
				break;
			case FIELD_TYPE_DATETIME:
				theType = @"datetime";
				break;
			case FIELD_TYPE_YEAR:
				theType = @"year";
				break;
			case FIELD_TYPE_VAR_STRING:
				theType = @"varstring";
				break;
			case FIELD_TYPE_STRING:
				theType = @"string";
				break;
			case FIELD_TYPE_TINY_BLOB:
				theType = @"tinyblob";
				break;
			case FIELD_TYPE_BLOB:
				theType = @"blob";
				break;
			case FIELD_TYPE_MEDIUM_BLOB:
				theType = @"mediumblob";
				break;
			case FIELD_TYPE_LONG_BLOB:
				theType = @"longblob";
				break;
			case FIELD_TYPE_SET:
				theType = @"set";
				break;
			case FIELD_TYPE_ENUM:
				theType = @"enum";
				break;
			case FIELD_TYPE_NULL:
				theType = @"null";
				break;
			case FIELD_TYPE_NEWDATE:
				theType = @"newdate";
				break;
			default:
				theType = @"unknown";
				NSLog (@"in fetchTypesAsArray : Unknown type for column %ld of the MCPResult, type = %ld", (NSInteger)i, (NSInteger)theField[i].type);
				break;
		}
		
		switch (aType) {
			case MCPTypeArray :
				[theTypes addObject:theType];
				break;
			case MCPTypeDictionary :
				[theTypes setObject:theType forKey:[mNames objectAtIndex:i]];
				break;
			default :
				[theTypes addObject:theType];
				break;
		}
	}
	
	return theTypes;
}

/**
 * Return an array of the fields' types.
 *
 * NB: Returned object is immutable.
 */
- (NSArray *)fetchTypesAsArray
{
	NSMutableArray *theArray = [self fetchTypesAsType:MCPTypeArray];
	
	return (theArray) ? [NSArray arrayWithArray:theArray] : nil;
}

/**
 * Return a dictionnary of the fields' types (keys are the fields' names).
 *
 * NB: Returned object is immutable.
 */
- (NSDictionary*) fetchTypesAsDictionary
{
	NSMutableDictionary *theDict = [self fetchTypesAsType:MCPTypeDictionary];
		
	return (theDict) ? [NSDictionary dictionaryWithDictionary:theDict] : nil;
}

/**
 * Return an array of dicts containg column data of the last executed query
 */
- (NSArray *)fetchResultFieldsStructure
{
	MYSQL_FIELD *theField;
	
	NSMutableArray *structureResult = [NSMutableArray array];
	
	NSUInteger i;
	NSUInteger numFields = mysql_num_fields(mResult);
	
	if (mResult == NULL) return nil;
	
	theField = mysql_fetch_fields(mResult);
	
	for (i=0; i < numFields; i++)
	{
		NSMutableDictionary *fieldStructure = [NSMutableDictionary dictionaryWithCapacity:39];
		
		/* Original column position */
		[fieldStructure setObject:[NSNumber numberWithInteger:i] forKey:@"datacolumnindex"];
		
		/* Name of column */
		[fieldStructure setObject:[self stringWithCString:theField[i].name] forKey:@"name"];
		// [fieldStructure setObject:[NSNumber numberWithUnsignedInt:theField[i].name_length] forKey:@"name_length"];
		
		/* Original column name, if an alias */ 
		[fieldStructure setObject:[self stringWithCString:theField[i].org_name] forKey:@"org_name"];
		// [fieldStructure setObject:[NSNumber numberWithUnsignedInt:theField[i].org_name_length] forKey:@"org_name_length"];
		
		/* Table of column if column was a field */
		[fieldStructure setObject:[self stringWithCString:theField[i].table] forKey:@"table"];
		// [fieldStructure setObject:[NSNumber numberWithUnsignedInt:theField[i].table_length] forKey:@"table_length"];
		
		/* Org table name, if table was an alias */
		[fieldStructure setObject:[self stringWithCString:theField[i].org_table] forKey:@"org_table"];
		// [fieldStructure setObject:[NSNumber numberWithUnsignedInt:theField[i].org_table_length] forKey:@"org_table_length"];
		
		/* Database for table */
		[fieldStructure setObject:[self stringWithCString:theField[i].db] forKey:@"db"];
		// [fieldStructure setObject:[NSNumber numberWithUnsignedInt:theField[i].db_length] forKey:@"db_length"];
		
		/* Catalog for table */
		// [fieldStructure setObject:[self stringWithCString:theField[i].catalog] forKey:@"catalog"];
		// [fieldStructure setObject:[NSNumber numberWithUnsignedInt:theField[i].catalog_length] forKey:@"catalog_length"];
		
		/* Default value (set by mysql_list_fields) */
		// [fieldStructure setObject:[self stringWithCString:theField[i].def] forKey:@"def"];
		// [fieldStructure setObject:[NSNumber numberWithUnsignedInt:theField[i].def_length] forKey:@"def_length"];
		
		/* Width of column (real length in bytes) */
		[fieldStructure setObject:[NSNumber numberWithUnsignedLongLong:theField[i].length] forKey:@"byte_length"];
		/* Width of column (as in create)*/
		[fieldStructure setObject:[NSNumber numberWithUnsignedLongLong:theField[i].length/[self findCharsetMaxByteLengthPerChar:theField[i].charsetnr]] 
						   forKey:@"char_length"];
		/* Max width (bytes) for selected set */
		[fieldStructure setObject:[NSNumber numberWithUnsignedLongLong:theField[i].max_length] forKey:@"max_byte_length"];
		/* Max width (chars) for selected set */
		// [fieldStructure setObject:[NSNumber numberWithUnsignedLongLong:theField[i].max_length/[self find_charsetMaxByteLengthPerChar:theField[i].charsetnr]] 
		// 		forKey:@"max_char_length"];
		
		/* Div flags */
		[fieldStructure setObject:[NSNumber numberWithUnsignedInt:theField[i].flags] forKey:@"flags"];
		[fieldStructure setObject:[NSNumber numberWithBool:(theField[i].flags & NOT_NULL_FLAG) ? YES : NO] forKey:@"null"];
		[fieldStructure setObject:[NSNumber numberWithBool:(theField[i].flags & PRI_KEY_FLAG) ? YES : NO] forKey:@"PRI_KEY_FLAG"];
		[fieldStructure setObject:[NSNumber numberWithBool:(theField[i].flags & UNIQUE_KEY_FLAG) ? YES : NO] forKey:@"UNIQUE_KEY_FLAG"];
		[fieldStructure setObject:[NSNumber numberWithBool:(theField[i].flags & MULTIPLE_KEY_FLAG) ? YES : NO] forKey:@"MULTIPLE_KEY_FLAG"];
		[fieldStructure setObject:[NSNumber numberWithBool:(theField[i].flags & BLOB_FLAG) ? YES : NO] forKey:@"BLOB_FLAG"];
		[fieldStructure setObject:[NSNumber numberWithBool:(theField[i].flags & UNSIGNED_FLAG) ? YES : NO] forKey:@"UNSIGNED_FLAG"];
		[fieldStructure setObject:[NSNumber numberWithBool:(theField[i].flags & ZEROFILL_FLAG) ? YES : NO] forKey:@"ZEROFILL_FLAG"];
		[fieldStructure setObject:[NSNumber numberWithBool:(theField[i].flags & BINARY_FLAG) ? YES : NO] forKey:@"BINARY_FLAG"];
		[fieldStructure setObject:[NSNumber numberWithBool:(theField[i].flags & ENUM_FLAG) ? YES : NO] forKey:@"ENUM_FLAG"];
		[fieldStructure setObject:[NSNumber numberWithBool:(theField[i].flags & AUTO_INCREMENT_FLAG) ? YES : NO] forKey:@"AUTO_INCREMENT_FLAG"];
		[fieldStructure setObject:[NSNumber numberWithBool:(theField[i].flags & SET_FLAG) ? YES : NO] forKey:@"SET_FLAG"];
		[fieldStructure setObject:[NSNumber numberWithBool:(theField[i].flags & NUM_FLAG) ? YES : NO] forKey:@"NUM_FLAG"];
		[fieldStructure setObject:[NSNumber numberWithBool:(theField[i].flags & PART_KEY_FLAG) ? YES : NO] forKey:@"PART_KEY_FLAG"];
		// [fieldStructure setObject:[NSNumber numberWithInt:(theField[i].flags & GROUP_FLAG) ? 1 : 0] forKey:@"GROUP_FLAG"];
		// [fieldStructure setObject:[NSNumber numberWithInt:(theField[i].flags & UNIQUE_FLAG) ? 1 : 0] forKey:@"UNIQUE_FLAG"];
		// [fieldStructure setObject:[NSNumber numberWithInt:(theField[i].flags & BINCMP_FLAG) ? 1 : 0] forKey:@"BINCMP_FLAG"];
		
		/* Number of decimals in field */
		[fieldStructure setObject:[NSNumber numberWithUnsignedInteger:theField[i].decimals] forKey:@"decimals"];
		
		/* Character set */
		[fieldStructure setObject:[NSNumber numberWithUnsignedInteger:theField[i].charsetnr] forKey:@"charsetnr"];
		[fieldStructure setObject:[self findCharsetName:theField[i].charsetnr] forKey:@"charset_name"];
		[fieldStructure setObject:[self findCharsetCollation:theField[i].charsetnr] forKey:@"charset_collation"];
		
		/* Table type */
		[fieldStructure setObject:[self mysqlTypeToStringForType:theField[i].type 
												   withCharsetNr:theField[i].charsetnr 
													   withFlags:theField[i].flags
													  withLength:theField[i].length 
								   ] forKey:@"type"];
		
		/* Table type group*/
		[fieldStructure setObject:[self mysqlTypeToGroupForType:theField[i].type 
												  withCharsetNr:theField[i].charsetnr 
													  withFlags:theField[i].flags
								   ] forKey:@"typegrouping"];
		
		[structureResult addObject:fieldStructure];
		
	}
	
	return structureResult;
	
}

/**
 * Return the MySQL flags of the column at the given index... Can be used to check if a number is signed or not...
 */
- (NSUInteger)fetchFlagsAtIndex:(NSUInteger)index
{
   NSUInteger theRet;
   NSUInteger theNumFields;
   MYSQL_FIELD *theField;
   
   if (mResult == NULL) {
	   // If no results, give an empty array. Maybe it's better to give a nil pointer?
      return (0);
   }
   
   theNumFields = [self numOfFields];
   theField = mysql_fetch_fields(mResult);
	
   if (index >= theNumFields) {
	   // Out of range... should raise an exception
      theRet = 0;
   }
   else {
      theRet = theField[index].flags;
   }
	
   return theRet;
}

/**
 *
 */
- (NSUInteger)fetchFlagsForKey:(NSString *)key
{
   NSUInteger theRet;
   NSUInteger theNumFields, index;
   MYSQL_FIELD *theField;
	
   if (mResult == NULL) {
	   // If no results, give an empty array. Maybe it's better to give a nil pointer?
      return (0);
   }
	
   if (mNames == NULL) {
      [self fetchFieldNames];
   }
	
   theNumFields = [self numOfFields];
   theField = mysql_fetch_fields(mResult);
	
   if ([mNames indexOfObject:key] == NSNotFound) {
	   // Non existent key... should raise an exception
      theRet = 0;
   }
   else {
	   index = [mNames indexOfObject:key];
	   
	   theRet = theField[index].flags;
   }
	
   return theRet;
}

/**
 * Return YES if the field with the given index is a BLOB. It should be used to discriminates between BLOBs 
 * and TEXTs.
 *
 * #{DEPRECATED}, This method is not consistent with the C API which is supposed to return YES for BOTH 
 * text and blob (and BTW is also deprecated)...
 *
 * #{NOTE} That the current version handles properly TEXT, and returns those as NSString (and not NSData as 
 * it used to be).
 */
- (BOOL)isBlobAtIndex:(NSUInteger)index
{
	BOOL theRet;
	NSUInteger theNumFields;
	MYSQL_FIELD	*theField;
	
	if (mResult == NULL) {
		// If no results, give an empty array. Maybe it's better to give a nil pointer?
		return (NO);
	}
	
	theNumFields = [self numOfFields];
	theField = mysql_fetch_fields(mResult);
	
	if (index >= theNumFields) {
		// Out of range... should raise an exception
		theRet = NO;
	}
	else {
		switch(theField[index].type) {
			case FIELD_TYPE_TINY_BLOB:
			case FIELD_TYPE_BLOB:
			case FIELD_TYPE_MEDIUM_BLOB:
			case FIELD_TYPE_LONG_BLOB:
				theRet = (theField[index].flags & BINARY_FLAG);
				break;
			default:
				theRet = NO;
				break;
		}
	}
	
	return theRet;
}

/**
 * Return YES if the field (by name) with the given index is a BLOB. It should be used to discriminates 
 * between BLOBs and TEXTs.
 *
 * #{DEPRECATED}, This method is not consistent with the C API which is supposed to return YES for BOTH 
 * text and blob (and BTW is also deprecated)...
 *
 * #{NOTE} That the current version handles properly TEXT, and returns those as NSString (and not NSData 
 * as it used to be).
 */
- (BOOL)isBlobForKey:(NSString *)key
{
	BOOL theRet;
	NSUInteger theNumFields, index;
	MYSQL_FIELD *theField;
	
	if (mResult == NULL) {
		// If no results, give an empty array. Maybe it's better to give a nil pointer?
		return (NO);
	}
	
	if (mNames == NULL) {
		[self fetchFieldNames];
	}
	
	theNumFields = [self numOfFields];
	theField = mysql_fetch_fields(mResult);
		
	if ([mNames indexOfObject:key] == NSNotFound) {
		// Non existent key... should raise an exception
		theRet = NO;
	}
	else {
		index = [mNames indexOfObject:key];
		
		switch(theField[index].type) {
			case FIELD_TYPE_TINY_BLOB:
			case FIELD_TYPE_BLOB:
			case FIELD_TYPE_MEDIUM_BLOB:
			case FIELD_TYPE_LONG_BLOB:
				theRet = (theField[index].flags & BINARY_FLAG);
				break;
			default:
				theRet = NO;
				break;
		}
	}
	
	return theRet;
}

#pragma mark -
#pragma mark Conversion

/**
 * Use the string encoding to convert the returned NSData to a string (for a TEXT field).
 */
- (NSString *)stringWithText:(NSData *)theTextData
{
	NSString *theString;
	
	if (theTextData == nil) {
		return nil;
	}
	
	theString = [[NSString alloc] initWithData:theTextData encoding:mEncoding];
	
	if (theString) {
		[theString autorelease];
	}
	
	return theString;
}

/**
 * Return a (long) string containing the table of results, first line being the fields name, next line(s) 
 * the row(s). Useful to have NSLog logging a MCPResult (example).
 */
- (NSString *)description
{
	if (mResult == NULL) {
		return @"This is an empty MCPResult\n";
	}
	else {
		NSMutableString  *theString = [NSMutableString stringWithCapacity:0];
		NSInteger				 i;
		NSArray			 *theRow;
		MYSQL_ROW_OFFSET thePosition;
		BOOL			 trunc = [MCPConnection truncateLongField];
		
		// First line, saying we are displaying a MCPResult
		[theString appendFormat:@"MCPResult: (encoding : %ld, dim %ld x %ld)\n", (long)mEncoding, (long)mNumOfFields, (long)[self numOfRows]];
		
		// Second line: the field names, tab separated
		[self fetchFieldNames];
		
		for (i=0; i<(mNumOfFields-1); i++) {
			[theString appendFormat:@"%@\t", [mNames objectAtIndex:i]];
		}
		
		[theString appendFormat:@"%@\n", [mNames objectAtIndex:i]];
		// Next lines, the records (saving current position to put it back after the full display)
		thePosition = mysql_row_tell(mResult);
		[self dataSeek:0];
		
		while (theRow = [self fetchRowAsArray]) 
		{
			id theField = [theRow objectAtIndex:i];
			
			if (trunc) {
				if (([theField isKindOfClass:[NSString class]]) && (kLengthOfTruncationForLog < [(NSString *)theField length])) {
					theField = [theField substringToIndex:kLengthOfTruncationForLog];
				}
				else if (([theField isKindOfClass:[NSData class]]) && (kLengthOfTruncationForLog < [(NSData *)theField length])) {
					theField = [NSData dataWithBytes:[theField bytes] length:kLengthOfTruncationForLog];
				}
			}
				
			for (i=0; i<(mNumOfFields - 1); i++) 
			{
				[theString appendFormat:@"%@\t", theField];
			}
			
			[theString appendFormat:@"%@\n", theField];
		}
		
		// Returning to the proper row
		mysql_row_seek(mResult, thePosition);
		
		return theString;
	}
}

/**
 * For internal use only. Transform a NSString to a C type string (ended with \0) using ethe character set 
 * from the MCPConnection. Lossy conversions are enabled.
 */
- (const char *)cStringFromString:(NSString *)theString
{
	NSMutableData *theData;
	
	if (!theString) {
		return (const char *)NULL;
	}
	
	theData = [NSMutableData dataWithData:[theString dataUsingEncoding:mEncoding allowLossyConversion:YES]];
	[theData increaseLengthBy:1];
	
	return (const char *)[theData bytes];
}

/**
 * Return a NSString from a C style string encoded with the character set of theMCPConnection.
 */
- (NSString *)stringWithCString:(const char *)theCString
{
	if (theCString == NULL) return @"";

	return [NSString stringWithCString:theCString encoding:mEncoding];
}

#pragma mark -
#pragma mark Other

/**
 * Convert a mysql_type to a string
 */
- (NSString *)mysqlTypeToStringForType:(NSUInteger)type withCharsetNr:(NSUInteger)charsetnr withFlags:(NSUInteger)flags withLength:(unsigned long long)length
{
	// BOOL isUnsigned = (flags & UNSIGNED_FLAG) != 0;
	// BOOL isZerofill = (flags & ZEROFILL_FLAG) != 0;
	
	switch (type) {
		case FIELD_TYPE_BIT:
			return @"BIT";
		case MYSQL_TYPE_DECIMAL:
		case MYSQL_TYPE_NEWDECIMAL:
			//return isUnsigned ? (isZerofill? @"DECIMAL UNSIGNED ZEROFILL" : @"DECIMAL UNSIGNED"): 
			return @"DECIMAL";
		case MYSQL_TYPE_TINY:
			// return isUnsigned ? (isZerofill? @"TINYINT UNSIGNED ZEROFILL" : @"TINYINT UNSIGNED"): 
			return @"TINYINT";
		case MYSQL_TYPE_SHORT:
			// return isUnsigned ? (isZerofill? @"SMALLINT UNSIGNED ZEROFILL" : @"SMALLINT UNSIGNED"): 
			return @"SMALLINT";
		case MYSQL_TYPE_LONG:
			// return isUnsigned ? (isZerofill? @"INT UNSIGNED ZEROFILL" : @"INT UNSIGNED"): 
			return @"INT";
		case MYSQL_TYPE_FLOAT:
			// return isUnsigned ? (isZerofill? @"FLOAT UNSIGNED ZEROFILL" : @"FLOAT UNSIGNED"): 
			return @"FLOAT";
		case MYSQL_TYPE_DOUBLE:
			// return isUnsigned ? (isZerofill? @"DOUBLE UNSIGNED ZEROFILL" : @"DOUBLE UNSIGNED"): 
			return @"DOUBLE";
		case MYSQL_TYPE_NULL:
			return @"NULL";
		case MYSQL_TYPE_TIMESTAMP:
			return @"TIMESTAMP";
		case MYSQL_TYPE_LONGLONG:
			// return isUnsigned ? (isZerofill? @"BIGINT UNSIGNED ZEROFILL" : @"BIGINT UNSIGNED") : 
			return @"BIGINT";
		case MYSQL_TYPE_INT24:
			// return isUnsigned ? (isZerofill? @"MEDIUMINT UNSIGNED ZEROFILL" : @"MEDIUMINT UNSIGNED") : 
			return @"MEDIUMINT";
		case MYSQL_TYPE_DATE:
			return @"DATE";
		case MYSQL_TYPE_TIME:
			return @"TIME";
		case MYSQL_TYPE_DATETIME:
			return @"DATETIME";
		case MYSQL_TYPE_TINY_BLOB:// should no appear over the wire
		case MYSQL_TYPE_MEDIUM_BLOB:// should no appear over the wire
		case MYSQL_TYPE_LONG_BLOB:// should no appear over the wire
		case MYSQL_TYPE_BLOB:
		{
			BOOL isBlob = (charsetnr == MAGIC_BINARY_CHARSET_NR);
			switch ((NSInteger)length/[self findCharsetMaxByteLengthPerChar:charsetnr]) {
				case 255: return isBlob? @"TINYBLOB":@"TINYTEXT";
				case 65535: return isBlob? @"BLOB":@"TEXT";
				case 16777215: return isBlob? @"MEDIUMBLOB":@"MEDIUMTEXT";
				case 4294967295: return isBlob? @"LONGBLOB":@"LONGTEXT";
				default:
					switch (length) {
						case 255: return isBlob? @"TINYBLOB":@"TINYTEXT";
						case 65535: return isBlob? @"BLOB":@"TEXT";
						case 16777215: return isBlob? @"MEDIUMBLOB":@"MEDIUMTEXT";
						case 4294967295: return isBlob? @"LONGBLOB":@"LONGTEXT";
						default:
							return @"UNKNOWN";
					}
			}
		}
		case MYSQL_TYPE_VAR_STRING:
			if (flags & ENUM_FLAG) {
				return @"ENUM";
			}
			if (flags & SET_FLAG) {
				return @"SET";
			}
			if (charsetnr == MAGIC_BINARY_CHARSET_NR) {
				return @"VARBINARY";
			}
			return @"VARCHAR";
		case MYSQL_TYPE_STRING:
			if (flags & ENUM_FLAG) {
				return @"ENUM";
			}
			if (flags & SET_FLAG) {
				return @"SET";
			}
			if ((flags & BINARY_FLAG) && charsetnr == MAGIC_BINARY_CHARSET_NR) {
				return @"BINARY";
			}
			return @"CHAR";
		case MYSQL_TYPE_ENUM:
			/* This should never happen */
			return @"ENUM";
		case MYSQL_TYPE_YEAR:
			return @"YEAR";
		case MYSQL_TYPE_SET:
			/* This should never happen */
			return @"SET";
		case MYSQL_TYPE_GEOMETRY:
			return @"GEOMETRY";
		default:
			return @"UNKNOWN";
	}
}

/**
 * Merge mysql_types into type groups
 */
- (NSString *)mysqlTypeToGroupForType:(NSUInteger)type withCharsetNr:(NSUInteger)charsetnr withFlags:(NSUInteger)flags
{
	switch(type){
		case FIELD_TYPE_BIT:
			return @"bit";
		case MYSQL_TYPE_TINY:
		case MYSQL_TYPE_SHORT:
		case MYSQL_TYPE_LONG:
		case MYSQL_TYPE_LONGLONG:
		case MYSQL_TYPE_INT24:
			return @"integer";
		case MYSQL_TYPE_FLOAT:
		case MYSQL_TYPE_DOUBLE:
		case MYSQL_TYPE_DECIMAL:
		case MYSQL_TYPE_NEWDECIMAL:
			return @"float";
		case MYSQL_TYPE_YEAR:
		case MYSQL_TYPE_DATETIME:
		case MYSQL_TYPE_TIME:
		case MYSQL_TYPE_DATE:
		case MYSQL_TYPE_TIMESTAMP:
			return @"date";
		case MYSQL_TYPE_VAR_STRING:
			if (flags & ENUM_FLAG) {
				return @"enum";
			}
			if (flags & SET_FLAG) {
				return @"enum";
			}
			if (charsetnr == MAGIC_BINARY_CHARSET_NR) {
				return @"binary";
			}
			return @"string";
		case MYSQL_TYPE_STRING:
			if (flags & ENUM_FLAG) {
				return @"enum";
			}
			if (flags & SET_FLAG) {
				return @"enum";
			}
			if ((flags & BINARY_FLAG) && charsetnr == MAGIC_BINARY_CHARSET_NR) {
				return @"binary";
			}
			return @"string";
		case MYSQL_TYPE_TINY_BLOB:   // should no appear over the wire
		case MYSQL_TYPE_MEDIUM_BLOB: // should no appear over the wire
		case MYSQL_TYPE_LONG_BLOB:   // should no appear over the wire
		case MYSQL_TYPE_BLOB:
		{
			if (charsetnr == MAGIC_BINARY_CHARSET_NR) {
				return @"blobdata";
			} else {
				return @"textdata";
			}
		}
		case MYSQL_TYPE_GEOMETRY:
			return @"geometry";
		default:
			return @"blobdata";
			
	}
}

/**
 * Convert a mysql_charsetnr into a charset name as string
 */
- (NSString *)findCharsetName:(NSUInteger)charsetnr
{
	const OUR_CHARSET * c = our_charsets60;
	
	do {
		if (c->nr == charsetnr)
			return [self stringWithCString:c->name];
		++c;
	} while (c[0].nr != 0);
	
	return @"UNKNOWN";
}

/**
 * Convert a mysql_charsetnr into a collation name as string
 */
- (NSString *)findCharsetCollation:(NSUInteger)charsetnr
{
	const OUR_CHARSET * c = our_charsets60;
	
	do {
		if (c->nr == charsetnr)
			return [self stringWithCString:c->collation];
		++c;
	} while (c[0].nr != 0);
	
	return @"UNKNOWN";
}

/**
 * Return the max byte length to store a char by using
 * a specific mysql_charsetnr
 */
- (NSUInteger)findCharsetMaxByteLengthPerChar:(NSUInteger)charsetnr
{
	const OUR_CHARSET * c = our_charsets60;
	
	do {
		if (c->nr == charsetnr)
			return c->char_maxlen;
		++c;
	} while (c[0].nr != 0);
	
	return 1;
}

#pragma mark -

/**
 * Do one really needs an explanation for this method? Which by the way you should not use...
 */
- (void) dealloc
{
	if (mResult) {
		mysql_free_result(mResult);
	}
	
	if (mNames) {
		[mNames autorelease];
	}
	
	[super dealloc];
}

@end