//
// $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
// More info at
#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];
mReturnDataAsStrings = NO;
mTimeZone = nil;
if (mResult) {
mysql_free_result(mResult);
mResult = NULL;
}
if (mNames) {
[mNames release];
mNames = nil;
}
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];
mReturnDataAsStrings = NO;
if (mResult) {
mysql_free_result(mResult);
mResult = NULL;
}
if (mNames) {
[mNames release];
mNames = nil;
}
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];
mReturnDataAsStrings = NO;
if (mResult) {
mysql_free_result(mResult);
mResult = NULL;
}
if (mNames) {
[mNames release];
mNames = nil;
}
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= theNumFields) {
// Out of range... should raise an exception
theRet = 0;
}
else {
theRet = theField[index].flags;
}
return theRet;
}
/**
*
*/
- (NSUInteger)fetchFlagsForKey:(NSString *)key
{
NSUInteger theRet;
NSUInteger 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 == nil) {
[self fetchFieldNames];
}
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 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 == nil) {
[self fetchFieldNames];
}
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
/**
* Set whether the result should return data types as strings. This may be useful
* for queries where the result may be returned in either string or data form, but
* will be converted to string for display and use anyway.
* Note that certain MySQL versions also return data types for strings - eg SHOW
* commands like SHOW CREATE TABLE or SHOW VARIABLES, and this conversion can be
* necessary there.
*/
- (void) setReturnDataAsStrings:(BOOL)alwaysConvertData
{
mReturnDataAsStrings = alwaysConvertData;
}
/**
* 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 release];
if (mTimeZone) [mTimeZone release];
[super dealloc];
}
@end