aboutsummaryrefslogtreecommitdiffstats
path: root/Frameworks/MCPKit/MCPFoundationKit/MCPResult.m
diff options
context:
space:
mode:
authorstuconnolly <stuart02@gmail.com>2009-07-21 16:47:11 +0000
committerstuconnolly <stuart02@gmail.com>2009-07-21 16:47:11 +0000
commit8b8f3e6cea540b17262aadf6d97a8ad28fe41c03 (patch)
treead6bb7f53b03924aa24d0cf5822a27b3bd453592 /Frameworks/MCPKit/MCPFoundationKit/MCPResult.m
parent383863f98dfc488db0181e01d39da1bb025d421b (diff)
downloadsequelpro-8b8f3e6cea540b17262aadf6d97a8ad28fe41c03.tar.gz
sequelpro-8b8f3e6cea540b17262aadf6d97a8ad28fe41c03.tar.bz2
sequelpro-8b8f3e6cea540b17262aadf6d97a8ad28fe41c03.zip
Merge framework integration branch back to trunk. Summary of changes:
- Includes all custom code from subclasses CMMCPConnection and CMMCPResult, meaning they have subsequently been removed from the project. - All previous Sequel Pro specific code in the above subclasses has been removed in favour of the delegate (currently set to TableDocumet) informing the framework of such information. - All references to CMMCPConnection and CMMCPResult have subsequently been changed to MCPConnection and MCPResult. - Framework includes MySQL 5.1.36 client libraries and source headers. - Framework is now built as a 4-way (32/64 bit, i386/PPC arch) binary. - All import references to <MCPKit_bundled/MCPKit_bundled.h> have been changed to <MCPKit/MCPKit.h>. - New script 'build-mysql-client.sh' can be used to build the MySQL client libraries from the MySQL source. See the script's header for a list of available options or run it with no arguments to display it's usage. Note that there are still a few changes to be made to the framework with regard to removing Sequel Pro specific calls to the delegate. These however can be made later on as they have no effect on functionality and are merely design changes. Also, note that any future development done on the framework should be made to be as 'generic' as possible, with no Sequel Pro specific references. This should allow the framework to be integrated into another project without the need for SP specific code.
Diffstat (limited to 'Frameworks/MCPKit/MCPFoundationKit/MCPResult.m')
-rw-r--r--Frameworks/MCPKit/MCPFoundationKit/MCPResult.m1360
1 files changed, 1360 insertions, 0 deletions
diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPResult.m b/Frameworks/MCPKit/MCPFoundationKit/MCPResult.m
new file mode 100644
index 00000000..f8158f7c
--- /dev/null
+++ b/Frameworks/MCPKit/MCPFoundationKit/MCPResult.m
@@ -0,0 +1,1360 @@
+//
+// $Id: MCPResult.m 1065 2009-07-19 10:58:17Z stuart02 $
+//
+// 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] retain];
+ [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;
+ }
+
+ if (mMySQLLocales == NULL) {
+ mMySQLLocales = [[MCPConnection getMySQLLocales] retain];
+ }
+
+ 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;
+ }
+
+ if (mMySQLLocales == NULL) {
+ mMySQLLocales = [[MCPConnection getMySQLLocales] retain];
+ }
+ }
+
+ 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;
+ }
+
+ if (mMySQLLocales == NULL) {
+ mMySQLLocales = [[MCPConnection getMySQLLocales] retain];
+ }
+ }
+
+ 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.
+ */
+- (unsigned int)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;
+ int 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 = [self stringWithCString:theData];
+ 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 : %d for column %d, send back a NSData object", (int)theField[i].type, (int)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
+{
+ int i;
+ unsigned int 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 %d", 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
+{
+ int 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:
+ 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 %d of the MCPResult, type = %d", (int)i, (int)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];
+
+ unsigned int i;
+ unsigned int 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 numberWithInt: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 numberWithUnsignedInt:theField[i].decimals] forKey:@"decimals"];
+
+ /* Character set */
+ [fieldStructure setObject:[NSNumber numberWithUnsignedInt: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...
+ */
+- (unsigned int)fetchFlagsAtIndex:(unsigned int)index
+{
+ unsigned int theRet;
+ unsigned int 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;
+}
+
+/**
+ *
+ */
+- (unsigned int)fetchFlagsForKey:(NSString *)key
+{
+ unsigned int theRet;
+ unsigned int 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:(unsigned int)index
+{
+ BOOL theRet;
+ unsigned int 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;
+ unsigned int 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];
+ int i;
+ NSArray *theRow;
+ MYSQL_ROW_OFFSET thePosition;
+ BOOL trunc = [MCPConnection truncateLongField];
+
+ // First line, saying we are displaying a MCPResult
+ [theString appendFormat:@"MCPResult: (encoding : %d, dim %d x %d)\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
+{
+ NSData *theData;
+ NSString *theString;
+
+ if (theCString == NULL) {
+ return @"";
+ }
+
+ theData = [NSData dataWithBytes:theCString length:(strlen(theCString))];
+ theString = [[NSString alloc] initWithData:theData encoding:mEncoding];
+
+ if (theString) {
+ [theString autorelease];
+ }
+
+ return theString;
+}
+
+#pragma mark -
+#pragma mark Other
+
+/**
+ * Convert a mysql_type to a string
+ */
+- (NSString *)mysqlTypeToStringForType:(unsigned int)type withCharsetNr:(unsigned int)charsetnr withFlags:(unsigned int)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:
+ //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 ((int)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:(unsigned int)type withCharsetNr:(unsigned int)charsetnr withFlags:(unsigned int)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:
+ 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:(unsigned int)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:(unsigned int)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
+ */
+- (unsigned int)findCharsetMaxByteLengthPerChar:(unsigned int)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];
+ }
+
+ if (mMySQLLocales) {
+ [mMySQLLocales autorelease];
+ }
+
+ [super dealloc];
+}
+
+@end