diff options
author | Bibiko <bibiko@eva.mpg.de> | 2010-10-07 14:15:07 +0000 |
---|---|---|
committer | Bibiko <bibiko@eva.mpg.de> | 2010-10-07 14:15:07 +0000 |
commit | 95d2e4acc393e91aa70ed4c71daa1f776454a936 (patch) | |
tree | c0462090392ae1e252d6ddff5af5887f84dc3ce1 | |
parent | b763c421f6fc2fc693ccbf89ffe38c64cd977ab8 (diff) | |
download | sequelpro-95d2e4acc393e91aa70ed4c71daa1f776454a936.tar.gz sequelpro-95d2e4acc393e91aa70ed4c71daa1f776454a936.tar.bz2 sequelpro-95d2e4acc393e91aa70ed4c71daa1f776454a936.zip |
• removed approach to query spatial data by using AsText() since it breaks some column definition approaches
- instead introduced a new MCPKit class MCPGeometryData
- up to now the spatial data will be displayed as hex bytes - work on it will come soon
-rw-r--r-- | Frameworks/MCPKit/MCPFoundationKit/MCPGeometryData.h | 39 | ||||
-rw-r--r-- | Frameworks/MCPKit/MCPFoundationKit/MCPGeometryData.m | 73 | ||||
-rw-r--r-- | Frameworks/MCPKit/MCPFoundationKit/MCPKit.h | 1 | ||||
-rw-r--r-- | Frameworks/MCPKit/MCPFoundationKit/MCPResult.m | 336 | ||||
-rw-r--r-- | Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.m | 3 | ||||
-rw-r--r-- | Source/SPCopyTable.m | 6 | ||||
-rw-r--r-- | Source/SPCustomQuery.m | 3 | ||||
-rw-r--r-- | Source/SPTableContent.m | 45 | ||||
-rw-r--r-- | sequel-pro.xcodeproj/project.pbxproj | 9 |
9 files changed, 319 insertions, 196 deletions
diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPGeometryData.h b/Frameworks/MCPKit/MCPFoundationKit/MCPGeometryData.h new file mode 100644 index 00000000..60a7febf --- /dev/null +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPGeometryData.h @@ -0,0 +1,39 @@ +// +// $Id$ +// +// MCPGeometryData.h +// sequel-pro +// +// Created by Hans-Jörg Bibiko on October 07, 2010 +// +// 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://code.google.com/p/sequel-pro/> + +#import <Cocoa/Cocoa.h> +#import <Foundation/Foundation.h> + +@interface MCPGeometryData : NSObject <NSCoding, NSCopying> +{ + char *geoBuffer; + NSUInteger bufferLength; +} + +- (id)initWithData:(NSData*)geoData; ++ (id)dataWithData:(NSData*)geoData; +- (NSString*)description; +- (NSUInteger)length; + +@end diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPGeometryData.m b/Frameworks/MCPKit/MCPFoundationKit/MCPGeometryData.m new file mode 100644 index 00000000..9f7ac19a --- /dev/null +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPGeometryData.m @@ -0,0 +1,73 @@ +// +// $Id$ +// +// MCPGeometryData.m +// sequel-pro +// +// Created by Hans-Jörg Bibiko on October 07, 2010 +// +// 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://code.google.com/p/sequel-pro/> + +#import "MCPGeometryData.h" + + +@implementation MCPGeometryData + +- (id)copyWithZone:(NSZone *)zone { return [self retain]; } + +- (id)init +{ + if ((self = [super init])) { + geoBuffer = nil; + bufferLength = 0; + } + return self; +} + +- (id)initWithData:(NSData*)geoData +{ + if ((self = [self init])) { + bufferLength = [geoData length]; + geoBuffer = malloc(bufferLength); + memcpy(geoBuffer, [geoData bytes], bufferLength); + } + return self; +} + + ++ (id)dataWithData:(NSData*)geoData +{ + return [[[MCPGeometryData alloc] initWithData:geoData] autorelease]; +} + +- (NSString*)description +{ + return [[NSData dataWithBytes:geoBuffer length:bufferLength] description]; +} + +- (NSUInteger)length +{ + return bufferLength; +} + +- (void)dealloc +{ + if(geoBuffer && bufferLength) free(geoBuffer); + [super dealloc]; +} + +@end diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPKit.h b/Frameworks/MCPKit/MCPFoundationKit/MCPKit.h index 11d2c72e..b83ebb69 100644 --- a/Frameworks/MCPKit/MCPFoundationKit/MCPKit.h +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPKit.h @@ -37,5 +37,6 @@ #import <MCPKit/MCPResultPlus.h> #import <MCPKit/MCPFastQueries.h> #import <MCPKit/MCPConnectionProxy.h> +#import <MCPKit/MCPGeometryData.h> #import "mysql.h" diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPResult.m b/Frameworks/MCPKit/MCPFoundationKit/MCPResult.m index be8a85eb..82b0636e 100644 --- a/Frameworks/MCPKit/MCPFoundationKit/MCPResult.m +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPResult.m @@ -30,6 +30,7 @@ #import "MCPNull.h" #import "MCPNumber.h" #import "MCPResult.h" +#import "MCPGeometryData.h" NSCalendarDate *MCPYear0000; @@ -196,28 +197,28 @@ const OUR_CHARSET our_charsets60[] = /** * 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. + * 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]; + * 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); + * 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++; + * i++; * } */ @@ -245,12 +246,12 @@ const OUR_CHARSET our_charsets60[] = mEncoding = [MCPConnection defaultMySQLEncoding]; mReturnDataAsStrings = NO; mTimeZone = nil; - + if (mResult) { mysql_free_result(mResult); mResult = NULL; } - + if (mNames) { [mNames release]; mNames = nil; @@ -258,12 +259,12 @@ const OUR_CHARSET our_charsets60[] = mNumOfFields = 0; } - - return self; + + return self; } /** - * Initialise a MCPResult, it is used internally by MCPConnection !{queryString:} method: the only proper + * 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 @@ -272,19 +273,19 @@ const OUR_CHARSET our_charsets60[] = 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); } @@ -292,12 +293,12 @@ const OUR_CHARSET our_charsets60[] = mNumOfFields = 0; } } - + return self; } /** - * This metod is used internally by MCPConnection object when it have already a MYSQL_RES object to initialise + * 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. */ @@ -307,19 +308,19 @@ const OUR_CHARSET our_charsets60[] = 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); } @@ -327,8 +328,8 @@ const OUR_CHARSET our_charsets60[] = mNumOfFields = 0; } } - - return self; + + return self; } #pragma mark - @@ -342,7 +343,7 @@ const OUR_CHARSET our_charsets60[] = if (mResult) { return mysql_num_rows(mResult); } - + return 0; } @@ -354,7 +355,7 @@ const OUR_CHARSET our_charsets60[] = if (mResult) { return mNumOfFields = mysql_num_fields(mResult); } - + return mNumOfFields = 0; } @@ -376,23 +377,23 @@ const OUR_CHARSET our_charsets60[] = */ - (id)fetchRowAsType:(MCPReturnType)aType { - MYSQL_ROW theRow; - unsigned long *theLengths; - MYSQL_FIELD *theField; - NSInteger i; - id theReturn; - + 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]; @@ -408,13 +409,13 @@ const OUR_CHARSET our_charsets60[] = 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 { @@ -422,7 +423,7 @@ const OUR_CHARSET our_charsets60[] = //char *theUselLess; memcpy(theData, theRow[i],theLengths[i]); theData[theLengths[i]] = '\0'; - + switch (theField[i].type) { case FIELD_TYPE_TINY: case FIELD_TYPE_SHORT: @@ -445,60 +446,64 @@ const OUR_CHARSET our_charsets60[] = 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 the field is TEXT and NOT BLOB, or if force-return-as-string is // enabled, return a NSString instead of NSData - if (mReturnDataAsStrings || !(theField[i].flags & BINARY_FLAG)) { + if (mReturnDataAsStrings || !(theField[i].flags & BINARY_FLAG)) { theCurrentObj = [self stringWithText:theCurrentObj]; } - + break; - + case FIELD_TYPE_NULL: theCurrentObj = [NSNull null]; break; - + + case FIELD_TYPE_GEOMETRY: + theCurrentObj = [MCPGeometryData dataWithData:[NSData dataWithBytes:theData length:theLengths[i]]]; + 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 + * 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). @@ -508,12 +513,12 @@ const OUR_CHARSET our_charsets60[] = - (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 + * 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). @@ -541,20 +546,20 @@ const OUR_CHARSET our_charsets60[] = 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); - + theField = mysql_fetch_fields(mResult); + for (i=0; i<theNumFields; i++) { NSString *theName = [self stringWithCString:theField[i].name]; if ((theName) && (![theName isEqualToString:@""])) { @@ -564,12 +569,12 @@ const OUR_CHARSET our_charsets60[] = [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 + * 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 @@ -579,12 +584,12 @@ const OUR_CHARSET our_charsets60[] = 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]; @@ -600,9 +605,9 @@ const OUR_CHARSET our_charsets60[] = theTypes = [NSMutableArray arrayWithCapacity:mNumOfFields]; break; } - + theField = mysql_fetch_fields(mResult); - + for (i=0; i<mNumOfFields; i++) { NSString *theType; switch (theField[i].type) { @@ -676,12 +681,15 @@ const OUR_CHARSET our_charsets60[] = case FIELD_TYPE_NEWDATE: theType = @"newdate"; break; + case FIELD_TYPE_GEOMETRY: + theType = @"geometry"; + 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]; @@ -694,7 +702,7 @@ const OUR_CHARSET our_charsets60[] = break; } } - + return theTypes; } @@ -706,7 +714,7 @@ const OUR_CHARSET our_charsets60[] = - (NSArray *)fetchTypesAsArray { NSMutableArray *theArray = [self fetchTypesAsType:MCPTypeArray]; - + return (theArray) ? [NSArray arrayWithArray:theArray] : nil; } @@ -718,7 +726,7 @@ const OUR_CHARSET our_charsets60[] = - (NSDictionary*) fetchTypesAsDictionary { NSMutableDictionary *theDict = [self fetchTypesAsType:MCPTypeDictionary]; - + return (theDict) ? [NSDictionary dictionaryWithDictionary:theDict] : nil; } @@ -728,62 +736,62 @@ const OUR_CHARSET our_charsets60[] = - (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 */ + + /* 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]] + [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]] + // [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"]; @@ -802,34 +810,34 @@ const OUR_CHARSET our_charsets60[] = // [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 + [fieldStructure setObject:[self mysqlTypeToStringForType:theField[i].type + withCharsetNr:theField[i].charsetnr withFlags:theField[i].flags - withLength:theField[i].length + withLength:theField[i].length ] forKey:@"type"]; - + /* Table type group*/ - [fieldStructure setObject:[self mysqlTypeToGroupForType:theField[i].type - withCharsetNr:theField[i].charsetnr + [fieldStructure setObject:[self mysqlTypeToGroupForType:theField[i].type + withCharsetNr:theField[i].charsetnr withFlags:theField[i].flags ] forKey:@"typegrouping"]; - + [structureResult addObject:fieldStructure]; - + } - + return structureResult; - + } /** @@ -840,15 +848,15 @@ const OUR_CHARSET our_charsets60[] = 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; @@ -856,7 +864,7 @@ const OUR_CHARSET our_charsets60[] = else { theRet = theField[index].flags; } - + return theRet; } @@ -868,39 +876,39 @@ const OUR_CHARSET our_charsets60[] = 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 + * 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 + * #{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 + * #{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 @@ -908,15 +916,15 @@ const OUR_CHARSET our_charsets60[] = 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; @@ -934,18 +942,18 @@ const OUR_CHARSET our_charsets60[] = break; } } - + return theRet; } /** - * Return YES if the field (by name) with the given index is a BLOB. It should be used to discriminates + * 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 + * #{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 + * #{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 @@ -953,25 +961,25 @@ const OUR_CHARSET our_charsets60[] = 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: @@ -984,7 +992,7 @@ const OUR_CHARSET our_charsets60[] = break; } } - + return theRet; } @@ -1010,22 +1018,22 @@ const OUR_CHARSET our_charsets60[] = - (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) + * 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 @@ -1039,26 +1047,26 @@ const OUR_CHARSET our_charsets60[] = 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]) + + while (theRow = [self fetchRowAsArray]) { id theField = [theRow objectAtIndex:i]; - + if (trunc) { if (([theField isKindOfClass:[NSString class]]) && (kLengthOfTruncationForLog < [(NSString *)theField length])) { theField = [theField substringToIndex:kLengthOfTruncationForLog]; @@ -1067,37 +1075,37 @@ const OUR_CHARSET our_charsets60[] = theField = [NSData dataWithBytes:[theField bytes] length:kLengthOfTruncationForLog]; } } - - for (i=0; i<(mNumOfFields - 1); i++) + + 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 + * 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]; } @@ -1121,38 +1129,38 @@ const OUR_CHARSET our_charsets60[] = { // 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 isUnsigned ? (isZerofill? @"DECIMAL UNSIGNED ZEROFILL" : @"DECIMAL UNSIGNED"): return @"DECIMAL"; case MYSQL_TYPE_TINY: - // return isUnsigned ? (isZerofill? @"TINYINT UNSIGNED ZEROFILL" : @"TINYINT UNSIGNED"): + // return isUnsigned ? (isZerofill? @"TINYINT UNSIGNED ZEROFILL" : @"TINYINT UNSIGNED"): return @"TINYINT"; case MYSQL_TYPE_SHORT: - // return isUnsigned ? (isZerofill? @"SMALLINT UNSIGNED ZEROFILL" : @"SMALLINT UNSIGNED"): + // return isUnsigned ? (isZerofill? @"SMALLINT UNSIGNED ZEROFILL" : @"SMALLINT UNSIGNED"): return @"SMALLINT"; case MYSQL_TYPE_LONG: - // return isUnsigned ? (isZerofill? @"INT UNSIGNED ZEROFILL" : @"INT UNSIGNED"): + // return isUnsigned ? (isZerofill? @"INT UNSIGNED ZEROFILL" : @"INT UNSIGNED"): return @"INT"; case MYSQL_TYPE_FLOAT: - // return isUnsigned ? (isZerofill? @"FLOAT UNSIGNED ZEROFILL" : @"FLOAT UNSIGNED"): + // return isUnsigned ? (isZerofill? @"FLOAT UNSIGNED ZEROFILL" : @"FLOAT UNSIGNED"): return @"FLOAT"; case MYSQL_TYPE_DOUBLE: - // return isUnsigned ? (isZerofill? @"DOUBLE UNSIGNED ZEROFILL" : @"DOUBLE UNSIGNED"): + // 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 isUnsigned ? (isZerofill? @"BIGINT UNSIGNED ZEROFILL" : @"BIGINT UNSIGNED") : return @"BIGINT"; case MYSQL_TYPE_INT24: - // return isUnsigned ? (isZerofill? @"MEDIUMINT UNSIGNED ZEROFILL" : @"MEDIUMINT UNSIGNED") : + // return isUnsigned ? (isZerofill? @"MEDIUMINT UNSIGNED ZEROFILL" : @"MEDIUMINT UNSIGNED") : return @"MEDIUMINT"; case MYSQL_TYPE_DATE: return @"DATE"; @@ -1281,7 +1289,7 @@ const OUR_CHARSET our_charsets60[] = return @"geometry"; default: return @"blobdata"; - + } } @@ -1291,13 +1299,13 @@ const OUR_CHARSET our_charsets60[] = - (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"; } @@ -1307,13 +1315,13 @@ const OUR_CHARSET our_charsets60[] = - (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"; } @@ -1324,13 +1332,13 @@ const OUR_CHARSET our_charsets60[] = - (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; } diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.m b/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.m index 5883bdc2..7822d896 100644 --- a/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.m +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.m @@ -27,6 +27,7 @@ #import "MCPConnection.h" #import "MCPNull.h" #import "MCPNumber.h" +#import "MCPGeometryData.h" /** * IMPORTANT NOTE @@ -321,7 +322,7 @@ void _bytes2bin(Byte *n, NSUInteger nbytes, NSUInteger len, char *buf); break; case FIELD_TYPE_GEOMETRY: - cellData = [NSData dataWithBytes:theData length:fieldLengths[i]]; + cellData = [MCPGeometryData dataWithData:[NSData dataWithBytes:theData length:fieldLengths[i]]]; break; default: diff --git a/Source/SPCopyTable.m b/Source/SPCopyTable.m index e53e714a..9d014839 100644 --- a/Source/SPCopyTable.m +++ b/Source/SPCopyTable.m @@ -669,8 +669,12 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003; // Retrieve the cell's content contentString = [tableStorage cellDataAtRow:i column:columnIndex]; + // TODO it's temporarily + if ([contentString isKindOfClass:[MCPGeometryData class]]) + contentString = [contentString description]; + // Replace NULLs with their placeholder string - if ([contentString isNSNull]) { + else if ([contentString isNSNull]) { contentString = [prefs objectForKey:SPNullValue]; // Same for cells for which loading has been deferred - likely blobs diff --git a/Source/SPCustomQuery.m b/Source/SPCustomQuery.m index 5f658a1d..44ba5987 100644 --- a/Source/SPCustomQuery.m +++ b/Source/SPCustomQuery.m @@ -1847,6 +1847,9 @@ if ([theValue isNSNull]) return [prefs objectForKey:SPNullValue]; + if ([theValue isKindOfClass:[MCPGeometryData class]]) + return [theValue description]; + return theValue; } else { diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m index b620cf0b..cc5eb6bd 100644 --- a/Source/SPTableContent.m +++ b/Source/SPTableContent.m @@ -2716,43 +2716,27 @@ */ - (NSString *)fieldListForQuery { - NSInteger i; - NSMutableArray *fields = [NSMutableArray array]; - NSArray *columnNames = [tableDataInstance columnNames]; - BOOL hasGeometryFields = NO; + if (([prefs boolForKey:SPLoadBlobsAsNeeded]) && [dataColumns count]) { - if (([prefs boolForKey:SPLoadBlobsAsNeeded]) && ([dataColumns count] > 0)) { + NSMutableArray *fields = [NSMutableArray arrayWithCapacity:[dataColumns count]]; + BOOL tableHasBlobs = NO; + NSString *fieldName; - for (i = 0 ; i < [columnNames count]; i++) - { - if (![tableDataInstance columnIsBlobOrText:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"]] ) { - if([tableDataInstance columnIsGeometry:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"]]) - [fields addObject:[NSString stringWithFormat:@"AsText(%@)", [NSArrayObjectAtIndex(columnNames, i) backtickQuotedString]]]; - else - [fields addObject:[NSArrayObjectAtIndex(columnNames, i) backtickQuotedString]]; - } + for (NSDictionary* field in dataColumns) + if (![tableDataInstance columnIsBlobOrText:fieldName = [field objectForKey:@"name"]] ) + [fields addObject:[fieldName backtickQuotedString]]; else { // For blob/text fields, select a null placeholder so the column count is still correct [fields addObject:@"NULL"]; + tableHasBlobs = YES; } - } - return [fields componentsJoinedByString:@","]; - } else { + return (tableHasBlobs) ? [fields componentsJoinedByString:@", "] : @"*"; - for (i = 0 ; i < [columnNames count]; i++) - { - if([tableDataInstance columnIsGeometry:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"]]) { - [fields addObject:[NSString stringWithFormat:@"AsText(%@)", [NSArrayObjectAtIndex(columnNames, i) backtickQuotedString]]]; - hasGeometryFields = YES; - } - else - [fields addObject:[NSArrayObjectAtIndex(columnNames, i) backtickQuotedString]]; - } - if(hasGeometryFields) - return [fields componentsJoinedByString:@","]; - else - return @"*"; + } + else { + + return @"*"; } } @@ -3416,6 +3400,9 @@ theValue = SPDataStorageObjectAtRowAndColumn(tableValues, rowIndex, columnIndex); } + if([theValue isKindOfClass:[MCPGeometryData class]]) + return [theValue description]; + if ([theValue isNSNull]) return [prefs objectForKey:SPNullValue]; diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj index a2cca3d9..868c7ff0 100644 --- a/sequel-pro.xcodeproj/project.pbxproj +++ b/sequel-pro.xcodeproj/project.pbxproj @@ -378,6 +378,8 @@ BCD06FC7120AAACB00C73602 /* SPStringAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1789343A0F30C1DD0097539A /* SPStringAdditions.h */; }; BCD0AD490FBBFC340066EA5C /* SPSQLTokenizer.l in Sources */ = {isa = PBXBuildFile; fileRef = BCD0AD480FBBFC340066EA5C /* SPSQLTokenizer.l */; }; BCE0025D11173D2A009DA533 /* SPFieldMapperController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCE0025C11173D2A009DA533 /* SPFieldMapperController.m */; }; + BCE97AEE125DC4EC0091ED3C /* MCPGeometryData.h in Headers */ = {isa = PBXBuildFile; fileRef = BCE97AEC125DC4EC0091ED3C /* MCPGeometryData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BCE97AEF125DC4EC0091ED3C /* MCPGeometryData.m in Sources */ = {isa = PBXBuildFile; fileRef = BCE97AED125DC4EC0091ED3C /* MCPGeometryData.m */; }; BCEC862012115A30002561DA /* SPQLPluginConnectionBundleWindowTemplate.html in Resources */ = {isa = PBXBuildFile; fileRef = BCEC861F12115A30002561DA /* SPQLPluginConnectionBundleWindowTemplate.html */; }; BCEF78C6115215CA0023F8C2 /* network-small.tif in Resources */ = {isa = PBXBuildFile; fileRef = BCEF78C5115215CA0023F8C2 /* network-small.tif */; }; /* End PBXBuildFile section */ @@ -1021,6 +1023,8 @@ BCD0AD4A0FBBFC480066EA5C /* SPSQLTokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSQLTokenizer.h; sourceTree = "<group>"; }; BCE0025B11173D2A009DA533 /* SPFieldMapperController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPFieldMapperController.h; sourceTree = "<group>"; }; BCE0025C11173D2A009DA533 /* SPFieldMapperController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPFieldMapperController.m; sourceTree = "<group>"; }; + BCE97AEC125DC4EC0091ED3C /* MCPGeometryData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCPGeometryData.h; sourceTree = "<group>"; }; + BCE97AED125DC4EC0091ED3C /* MCPGeometryData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MCPGeometryData.m; sourceTree = "<group>"; }; BCEC861D12115A2B002561DA /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = English; path = English.lproj/SPQLPluginConnectionBundleWindowTemplate.html; sourceTree = "<group>"; }; BCEC862112115A3D002561DA /* German */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = German; path = German.lproj/SPQLPluginConnectionBundleWindowTemplate.html; sourceTree = "<group>"; }; BCEF78C5115215CA0023F8C2 /* network-small.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "network-small.tif"; sourceTree = "<group>"; }; @@ -1468,6 +1472,8 @@ 583B779810386B0200B21F7E /* MCPStreamingResult.m */, 17DCC5C5115C202700F89A00 /* MCPStringAdditions.h */, 17B7B5C6101603B200F057DE /* MCPConnectionProxy.h */, + BCE97AEC125DC4EC0091ED3C /* MCPGeometryData.h */, + BCE97AED125DC4EC0091ED3C /* MCPGeometryData.m */, ); path = MCPFoundationKit; sourceTree = "<group>"; @@ -2179,6 +2185,7 @@ 17B7B5F1101603D200F057DE /* typelib.h in Headers */, 17DCC5C7115C202700F89A00 /* MCPStringAdditions.h in Headers */, 58587B5A11B4437C00D129ED /* NSNotificationAdditions.h in Headers */, + BCE97AEE125DC4EC0091ED3C /* MCPGeometryData.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2351,7 +2358,6 @@ isa = PBXProject; buildConfigurationList = C05733CB08A9546B00998B17 /* Build configuration list for PBXProject "sequel-pro" */; compatibilityVersion = "Xcode 3.1"; - developmentRegion = English; hasScannedForEncodings = 1; knownRegions = ( English, @@ -2586,6 +2592,7 @@ 17B7B5E1101603B200F057DE /* MCPResultPlus.m in Sources */, 583B77D4103870C800B21F7E /* MCPStreamingResult.m in Sources */, 58587B5B11B4437C00D129ED /* NSNotificationAdditions.m in Sources */, + BCE97AEF125DC4EC0091ED3C /* MCPGeometryData.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; |