}
- (void)primaryCloseChannel {
+ if ([self isFetchInProgress])
+ [self cancelFetch];
+
if (self->_connection != NULL) {
mysql_close(self->_connection);
#if 0
/* fetching rows */
-- (NSException *)_makeMySQL4Step {
- NSString *r;
- const char *em;
- int rc;
-
-#if 1
-# warning IMPLEMENT ME
-#else
- rc = sqlite3_step(self->statement);
-#endif
-#if 0
- NSLog(@"STEP: %i (row=%i, done=%i, mis=%i)", rc,
- SQLITE_ROW, SQLITE_DONE, SQLITE_MISUSE);
-#endif
-
-#if 1
-# warning IMPLEMENT ME
-#else
- if (rc == SQLITE_ROW) {
- self->hasPendingRow = YES;
- self->isDone = NO;
- return nil /* no error */;
- }
- if (rc == SQLITE_DONE) {
- self->hasPendingRow = NO;
- self->isDone = YES;
- return nil /* no error */;
- }
-
- if (rc == SQLITE_ERROR)
- r = [NSString stringWithUTF8String:sqlite3_errmsg(self->_connection)];
- else if (rc == SQLITE_MISUSE)
- r = @"The MySQL4 step function was called in an incorrect way";
- else if (rc == SQLITE_BUSY)
- r = @"The MySQL4 is busy.";
- else
- r = [NSString stringWithFormat:@"Unexpected MySQL4 error: %i", rc];
-
- if ((em = sqlite3_errmsg(self->_connection)) != NULL)
- r = [r stringByAppendingFormat:@": %s", em];
-#endif
-
- return [MySQL4Exception exceptionWithName:@"FetchFailed"
- reason:r userInfo:nil];
-}
-
- (void)cancelFetch {
+ self->fields = NULL; /* apparently we do not need to free those */
+
if (self->results != NULL) {
mysql_free_result(self->results);
self->results = NULL;
}
-
- self->isDone = NO;
- self->hasPendingRow = NO;
[super cancelFetch];
}
+- (MYSQL_FIELD *)_fetchFields {
+ if (self->results == NULL)
+ return NULL;
+
+ if (self->fields != NULL)
+ return self->fields;
+
+ self->fields = mysql_fetch_fields(self->results);
+ self->fieldCount = mysql_num_fields(self->results);
+ return self->fields;
+}
+
- (NSArray *)describeResults {
// TODO: make exception-less method
- int cnt, fieldCount;
+ MYSQL_FIELD *mfields;
+ int cnt;
NSMutableArray *result = nil;
NSMutableDictionary *usedNames = nil;
NSNumber *yesObj;
if (![self isFetchInProgress]) {
[MySQL4Exception raise:@"NoFetchInProgress"
format:@"No fetch in progress (channel=%@)", self];
- }
-
- /* we need to fetch a row to get the info */
-
- if (!self->hasPendingRow) {
- NSException *error;
-
- if ((error = [self _makeMySQL4Step]) != nil) {
- [self cancelFetch];
- [error raise]; // raise error, TODO: make exception-less method
- return nil;
- }
- }
- if (!self->hasPendingRow) /* no rows available */
return nil;
+ }
-#if 1
-# warning IMPLEMENT ME
-#else
- fieldCount = sqlite3_column_count(self->statement);
-#endif
-
- /* old code below */
+ if ((mfields = [self _fetchFields]) == NULL) {
+ [MySQL4Exception raise:@"NoFieldInfo"
+ format:@"Failed to fetch field info (channel=%@)", self];
+ return nil;
+ }
result = [[NSMutableArray alloc] initWithCapacity:fieldCount];
usedNames = [[NSMutableDictionary alloc] initWithCapacity:fieldCount];
NSString *columnName = nil;
NSString *attrName = nil;
-#if 1
-# warning IMPLEMENT ME
-#else
- columnName = [NSString stringWithCString:
- sqlite3_column_name(self->statement, cnt)];
-#endif
+ columnName = [NSString stringWithUTF8String:mfields[cnt].name];
attrName = [columnName _mySQL4ModelMakeInstanceVarName];
if ([[usedNames objectForKey:attrName] boolValue]) {
[attribute setName:attrName];
[attribute setColumnName:columnName];
+ [attribute setAllowsNull:
+ (mfields[cnt].flags & NOT_NULL_FLAG) ? NO : YES];
+
+ /*
+ We also know whether a field:
+ is primary
+ is unique
+ is auto-increment
+ is zero-fill
+ is unsigned
+ */
+ if (mfields[cnt].flags & UNSIGNED_FLAG) {
+ NSLog(@"ERROR: MySQL4 field is marked unsigned (unsupported): %@",
+ attribute);
+ }
+
+ switch (mfields[cnt].type) {
+ case FIELD_TYPE_STRING:
+ [attribute setExternalType:@"CHAR"];
+ [attribute setValueClassName:@"NSString"];
+ // TODO: length etc
+ break;
+ case FIELD_TYPE_VAR_STRING:
+ [attribute setExternalType:@"VARCHAR"];
+ [attribute setValueClassName:@"NSString"];
+ // TODO: length etc
+ break;
+
+ case FIELD_TYPE_TINY:
+ [attribute setExternalType:@"TINY"];
+ [attribute setValueClassName:@"NSNumber"];
+ [attribute setValueType:@"c"];
+ break;
+ case FIELD_TYPE_SHORT:
+ [attribute setExternalType:@"SHORT"];
+ [attribute setValueClassName:@"NSNumber"];
+ [attribute setValueType:@"s"];
+ break;
+ case FIELD_TYPE_LONG:
+ [attribute setExternalType:@"LONG"];
+ [attribute setValueClassName:@"NSNumber"];
+ [attribute setValueType:@"l"];
+ break;
+ case FIELD_TYPE_INT24:
+ [attribute setExternalType:@"INT"];
+ [attribute setValueClassName:@"NSNumber"];
+ [attribute setValueType:@"i"]; // bumped
+ break;
+ case FIELD_TYPE_LONGLONG:
+ [attribute setExternalType:@"LONGLONG"];
+ [attribute setValueClassName:@"NSNumber"];
+ [attribute setValueType:@"q"];
+ break;
+ case FIELD_TYPE_DECIMAL:
+ [attribute setExternalType:@"DECIMAL"];
+ [attribute setValueClassName:@"NSNumber"];
+ [attribute setValueType:@"f"]; // TODO: need NSDecimalNumber here ...
+ break;
+ case FIELD_TYPE_FLOAT:
+ [attribute setExternalType:@"FLOAT"];
+ [attribute setValueClassName:@"NSNumber"];
+ [attribute setValueType:@"f"];
+ break;
+ case FIELD_TYPE_DOUBLE:
+ [attribute setExternalType:@"DOUBLE"];
+ [attribute setValueClassName:@"NSNumber"];
+ [attribute setValueType:@"d"];
+ break;
+
+ case FIELD_TYPE_TIMESTAMP:
+ [attribute setExternalType:@"TIMESTAMP"];
+ [attribute setValueClassName:@"NSCalendarDate"];
+ break;
+ case FIELD_TYPE_DATE:
+ [attribute setExternalType:@"DATE"];
+ [attribute setValueClassName:@"NSCalendarDate"];
+ break;
+ case FIELD_TYPE_DATETIME:
+ [attribute setExternalType:@"DATETIME"];
+ [attribute setValueClassName:@"NSCalendarDate"];
+ break;
+
+ case FIELD_TYPE_BLOB:
+ case FIELD_TYPE_TINY_BLOB:
+ case FIELD_TYPE_MEDIUM_BLOB:
+ case FIELD_TYPE_LONG_BLOB:
+ // TODO: length etc
+ if (mfields[cnt].flags & BINARY_FLAG) {
+ [attribute setExternalType:@"BLOB"];
+ [attribute setValueClassName:@"NSData"];
+ }
+ else {
+ [attribute setExternalType:@"TEXT"];
+ [attribute setValueClassName:@"NSString"];
+ }
+ break;
+
+ case FIELD_TYPE_NULL: // TODO: whats that?
+ case FIELD_TYPE_TIME:
+ case FIELD_TYPE_YEAR:
+ case FIELD_TYPE_SET:
+ case FIELD_TYPE_ENUM:
+ default:
+ NSLog(@"ERROR(%s): unexpected MySQL4 type at column %i: %@",
+ __PRETTY_FUNCTION__, cnt, attribute);
+ break;
+ }
+
#if 1
# warning IMPLEMENT ME
#else
[attribute setValueClassName:@"NSNull"];
break;
default:
- NSLog(@"ERROR(%s): unexpected MySQL4 type at column %i",
- __PRETTY_FUNCTION__, cnt);
- break;
}
#endif
[result addObject:attribute];
schema)
*/
// TODO: add a primaryFetchAttributesX method?
+ MYSQL_ROW rawRow;
NSMutableDictionary *row = nil;
NSException *error;
unsigned attrCount = [_attributes count];
unsigned cnt;
-
-#if 0
- if (self->statement == NULL) {
- NSLog(@"ERROR: no fetch in progress?");
+
+ if (self->results == NULL) {
+ NSLog(@"ERROR(%s): no fetch in progress?", __PRETTY_FUNCTION__);
[self cancelFetch];
return nil;
}
-#endif
+
+ /* raw fetch */
- if (!self->hasPendingRow && !self->isDone) {
- if ((error = [self _makeMySQL4Step]) != nil) {
- [self cancelFetch];
- [error raise]; // raise error, TODO: make exception-less method
+ if ((rawRow = mysql_fetch_row(self->results)) == NULL) {
+ // TODO: might need to close channel on connect exceptions
+ const char *error;
+
+ if ((error = mysql_error(self->_connection)) != NULL) {
+ [MySQL4Exception raise:@"FetchFailed"
+ format:@"%@",[NSString stringWithUTF8String:error]];
return nil;
}
- }
- if (self->isDone) { /* step was fine, but we are at the end */
+
+ /* regular end of result set */
[self cancelFetch];
return nil;
}
- self->hasPendingRow = NO; /* consume the row */
-
/* build row */
row = [NSMutableDictionary dictionaryWithCapacity:attrCount];
reason:@"MySQL4 connection is not open"
userInfo:nil];
}
-#if 0
- if (self->statement != NULL) {
+ if (self->results != NULL) {
return [MySQL4Exception exceptionWithName:@"CommandInProgressException"
reason:@"an evaluation is in progress"
userInfo:nil];
return NO;
}
-#endif
if ([self isFetchInProgress]) {
NSLog(@"WARNING: a fetch is still in progress: %@", self);
/* reset environment */
self->isFetchInProgress = NO;
- self->isDone = NO;
- self->hasPendingRow = NO;
/* start query */