aboutsummaryrefslogtreecommitdiffstats
path: root/Source/SPXMLExporter.m
diff options
context:
space:
mode:
Diffstat (limited to 'Source/SPXMLExporter.m')
-rw-r--r--Source/SPXMLExporter.m313
1 files changed, 205 insertions, 108 deletions
diff --git a/Source/SPXMLExporter.m b/Source/SPXMLExporter.m
index 1ff32018..962d5a53 100644
--- a/Source/SPXMLExporter.m
+++ b/Source/SPXMLExporter.m
@@ -35,6 +35,9 @@
@synthesize xmlDataArray;
@synthesize xmlTableName;
@synthesize xmlNULLString;
+@synthesize xmlOutputIncludeStructure;
+@synthesize xmlOutputIncludeContent;
+@synthesize xmlFormat;
/**
* Initialise an instance of SPXMLExporter using the supplied delegate.
@@ -62,13 +65,20 @@
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ BOOL isTableExport = NO;
+
NSArray *xmlRow = nil;
NSArray *fieldNames = nil;
NSString *dataConversionString = nil;
+
+ // Result sets
+ MCPResult *statusResult = nil;
+ MCPResult *structureResult = nil;
MCPStreamingResult *streamingResult = nil;
+ NSMutableArray *xmlTags = [NSMutableArray array];
NSMutableString *xmlString = [NSMutableString string];
- NSMutableString *xmlItem = [NSMutableString string];
+ NSMutableString *xmlItem = [NSMutableString string];
NSUInteger xmlRowCount = 0;
NSUInteger i, totalRows, currentRowIndex, lastProgressValue, currentPoolDataLength;
@@ -76,7 +86,8 @@
// Check to see if we have at least a table name or data array
if ((![self xmlTableName]) && (![self xmlDataArray]) ||
([[self xmlTableName] length] == 0) && ([[self xmlDataArray] count] == 0) ||
- (![self xmlNULLString]))
+ (([self xmlFormat] == SPXMLExportMySQLFormat) && ((![self xmlOutputIncludeStructure]) && (![self xmlOutputIncludeContent]))) ||
+ (([self xmlFormat] == SPXMLExportPlainFormat) && (![self xmlNULLString])))
{
[pool release];
return;
@@ -92,67 +103,105 @@
// Make a streaming request for the data if the data array isn't set
if ((![self xmlDataArray]) && [self xmlTableName]) {
- totalRows = [[[[connection queryString:[NSString stringWithFormat:@"SELECT COUNT(1) FROM %@", [[self xmlTableName] backtickQuotedString]]] fetchRowAsArray] objectAtIndex:0] integerValue];
+
+ isTableExport = YES;
+
+ totalRows = [[[[connection queryString:[NSString stringWithFormat:@"SELECT COUNT(1) FROM %@", [[self xmlTableName] backtickQuotedString]]] fetchRowAsArray] objectAtIndex:0] integerValue];
streamingResult = [connection streamingQueryString:[NSString stringWithFormat:@"SELECT * FROM %@", [[self xmlTableName] backtickQuotedString]] useLowMemoryBlockingStreaming:[self exportUsingLowMemoryBlockingStreaming]];
- [xmlString appendFormat:@"\t<table_data name=\"%@\">\n\n", [self xmlTableName]];
+ // Only include the structure if necessary
+ if (([self xmlFormat] == SPXMLExportMySQLFormat) && [self xmlOutputIncludeStructure]) {
+
+ structureResult = [connection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@", [[self xmlTableName] backtickQuotedString]]];
+ statusResult = [connection queryString:[NSString stringWithFormat:@"SHOW TABLE STATUS LIKE %@", [[self xmlTableName] tickQuotedString]]];
+
+ if ([structureResult numOfRows] && [statusResult numOfRows]) {
+
+ [statusResult dataSeek:0];
+ [structureResult dataSeek:0];
+
+ [xmlString appendFormat:@"\t<table_structure name=\"%@\">\n", [self xmlTableName]];
+
+ for (i = 0; i < [structureResult numOfRows]; i++)
+ {
+ NSDictionary *row = [structureResult fetchRowAsDictionary];
+
+ [xmlString appendFormat:@"\t\t<field field=\"%@\" type=\"%@\" null=\"%@\" key=\"%@\" default=\"%@\" extra=\"%@\" />\n",
+ [row objectForKey:@"Field"],
+ [row objectForKey:@"Type"],
+ [row objectForKey:@"Null"],
+ [row objectForKey:@"Key"],
+ [row objectForKey:@"Default"],
+ [row objectForKey:@"Extra"]];
+ }
+
+ NSDictionary *row = [statusResult fetchRowAsDictionary];
+
+ [xmlString appendFormat:@"\n\t\t<options name=\"%@\" engine=\"%@\" version=\"%@\" row_format=\"%@\" rows=\"%@\" avg_row_length=\"%@\" data_length=\"%@\" max_data_length=\"%@\" index_length=\"%@\" data_free=\"%@\" create_time=\"%@\" update_time=\"%@\" collation=\"%@\" create_options=\"%@\" comment=\"%@\" />\n",
+ [row objectForKey:@"Name"],
+ [row objectForKey:@"Engine"],
+ [row objectForKey:@"Version"],
+ [row objectForKey:@"Row_format"],
+ [row objectForKey:@"Rows"],
+ [row objectForKey:@"Avg_row_length"],
+ [row objectForKey:@"Data_length"],
+ [row objectForKey:@"Max_data_length"],
+ [row objectForKey:@"Index_length"],
+ [row objectForKey:@"Data_free"],
+ [row objectForKey:@"Create_time"],
+ [row objectForKey:@"Update_time"],
+ [row objectForKey:@"Collation"],
+ [row objectForKey:@"Create_options"],
+ [row objectForKey:@"Comment"]];
+
+ [xmlString appendFormat:@"\t</table_structure>\n\n"];
+ }
+ }
+
+ if (([self xmlFormat] == SPXMLExportMySQLFormat) && [self xmlOutputIncludeContent]) {
+ [xmlString appendFormat:@"\t<table_data name=\"%@\">\n\n", [self xmlTableName]];
+ }
+
+ [[self exportOutputFile] writeData:[xmlString dataUsingEncoding:[self exportOutputEncoding]]];
}
else {
totalRows = [[self xmlDataArray] count];
}
- // Set up an array of encoded field names as opening and closing tags
- fieldNames = ([self xmlDataArray]) ? [[self xmlDataArray] objectAtIndex:0] : [streamingResult fetchFieldNames];
+ // Only proceed to export the content if this is not a table export or it is and include content is selected
+ if ((!isTableExport) || (isTableExport && [self xmlOutputIncludeContent])) {
- [[self exportOutputFile] writeData:[xmlString dataUsingEncoding:[self exportOutputEncoding]]];
-
- // Set up the starting row, which is 0 for streaming result sets and
- // 1 for supplied arrays which include the column headers as the first row.
- currentRowIndex = 0;
-
- if ([self xmlDataArray]) currentRowIndex++;
-
- // Drop into the processing loop
- NSAutoreleasePool *xmlExportPool = [[NSAutoreleasePool alloc] init];
-
- currentPoolDataLength = 0;
-
- // Inform the delegate that we are about to start writing the data to disk
- [delegate performSelectorOnMainThread:@selector(xmlExportProcessWillBeginWritingData:) withObject:self waitUntilDone:NO];
-
- while (1)
- {
- // Check for cancellation flag
- if ([self isCancelled]) {
- if (streamingResult) {
- [connection cancelCurrentQuery];
- [streamingResult cancelResultLoad];
- }
-
- [xmlExportPool release];
- [pool release];
+ // Set up an array of encoded field names as opening and closing tags
+ fieldNames = ([self xmlDataArray]) ? NSArrayObjectAtIndex([self xmlDataArray], 0) : [streamingResult fetchFieldNames];
+
+ for (i = 0; i < [fieldNames count]; i++)
+ {
+ [xmlTags addObject:[NSMutableArray array]];
- return;
+ [NSArrayObjectAtIndex(xmlTags, i) addObject:[NSString stringWithFormat:@"\t\t<%@>", [[NSArrayObjectAtIndex(fieldNames, i) description] HTMLEscapeString]]];
+ [NSArrayObjectAtIndex(xmlTags, i) addObject:[NSString stringWithFormat:@"</%@>\n", [[NSArrayObjectAtIndex(fieldNames, i) description] HTMLEscapeString]]];
}
- // Retrieve the next row from the supplied data, either directly from the array...
- if ([self xmlDataArray]) {
- xmlRow = NSArrayObjectAtIndex([self xmlDataArray], currentRowIndex);
- }
- // Or by reading an appropriate row from the streaming result
- else {
- xmlRow = [streamingResult fetchNextRowAsArray];
-
- if (!xmlRow) break;
+ // If required, write an opening tag in the form of the table name
+ if ([self xmlFormat] == SPXMLExportPlainFormat) {
+ [[self exportOutputFile] writeData:[[NSString stringWithFormat:@"\t<%@>\n", ([self xmlTableName]) ? [[self xmlTableName] HTMLEscapeString] : @"custom"] dataUsingEncoding:[self exportOutputEncoding]]];
}
- // Get the cell count if we don't already have it stored
- if (!xmlRowCount) xmlRowCount = [xmlRow count];
+ // Set up the starting row, which is 0 for streaming result sets and
+ // 1 for supplied arrays which include the column headers as the first row.
+ currentRowIndex = 0;
+
+ if ([self xmlDataArray]) currentRowIndex++;
+
+ // Drop into the processing loop
+ NSAutoreleasePool *xmlExportPool = [[NSAutoreleasePool alloc] init];
+
+ currentPoolDataLength = 0;
- // Construct the row
- [xmlString setString:@"\t<row>\n"];
+ // Inform the delegate that we are about to start writing the data to disk
+ [delegate performSelectorOnMainThread:@selector(xmlExportProcessWillBeginWritingData:) withObject:self waitUntilDone:NO];
- for (i = 0; i < xmlRowCount; i++)
+ while (1)
{
// Check for cancellation flag
if ([self isCancelled]) {
@@ -167,76 +216,126 @@
return;
}
- BOOL dataIsNULL = NO;
- id data = NSArrayObjectAtIndex(xmlRow, i);
+ // Retrieve the next row from the supplied data, either directly from the array...
+ if ([self xmlDataArray]) {
+ xmlRow = NSArrayObjectAtIndex([self xmlDataArray], currentRowIndex);
+ }
+ // Or by reading an appropriate row from the streaming result
+ else {
+ xmlRow = [streamingResult fetchNextRowAsArray];
+
+ if (!xmlRow) break;
+ }
- // Retrieve the contents of this tag
- if ([data isKindOfClass:[NSData class]]) {
- dataConversionString = [[NSString alloc] initWithData:data encoding:[self exportOutputEncoding]];
+ // Get the cell count if we don't already have it stored
+ if (!xmlRowCount) xmlRowCount = [xmlRow count];
+
+ // Construct the row
+ [xmlString setString:@"\t<row>\n"];
+
+ for (i = 0; i < xmlRowCount; i++)
+ {
+ // Check for cancellation flag
+ if ([self isCancelled]) {
+ if (streamingResult) {
+ [connection cancelCurrentQuery];
+ [streamingResult cancelResultLoad];
+ }
+
+ [xmlExportPool release];
+ [pool release];
+
+ return;
+ }
- if (dataConversionString == nil) {
- dataConversionString = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
+ BOOL dataIsNULL = NO;
+ id data = NSArrayObjectAtIndex(xmlRow, i);
+
+ // Retrieve the contents of this tag
+ if ([data isKindOfClass:[NSData class]]) {
+ dataConversionString = [[NSString alloc] initWithData:data encoding:[self exportOutputEncoding]];
+
+ if (dataConversionString == nil) {
+ dataConversionString = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
+ }
+
+ [xmlItem setString:[NSString stringWithString:dataConversionString]];
+ [dataConversionString release];
+ }
+ else if ([data isKindOfClass:[NSNull class]]) {
+ dataIsNULL = YES;
+
+ if ([self xmlFormat] == SPXMLExportPlainFormat) {
+ [xmlItem setString:[self xmlNULLString]];
+ }
+ }
+ else if ([data isKindOfClass:[MCPGeometryData class]]) {
+ [xmlItem setString:[data wktString]];
+ }
+ else {
+ [xmlItem setString:[data description]];
}
- [xmlItem setString:[NSString stringWithString:dataConversionString]];
- [dataConversionString release];
- }
- else if ([data isKindOfClass:[NSNull class]]) {
- dataIsNULL = YES;
- }
- else if ([data isKindOfClass:[MCPGeometryData class]]) {
- [xmlItem setString:[data wktString]];
- }
- else {
- [xmlItem setString:[data description]];
+ if ([self xmlFormat] == SPXMLExportMySQLFormat) {
+ [xmlString appendFormat:@"\t\t<field name=\"%@\"", [[NSArrayObjectAtIndex(fieldNames, i) description] HTMLEscapeString]];
+
+ if (dataIsNULL) {
+ [xmlString appendString:@" xsi:nil=\"true\" \\>\n"];
+ }
+ else {
+ [xmlString appendFormat:@">%@</field>\n", [xmlItem HTMLEscapeString]];
+ }
+ }
+ else if ([self xmlFormat] == SPXMLExportPlainFormat) {
+ // Add the opening and closing tag and the contents to the XML string
+ [xmlString appendString:NSArrayObjectAtIndex(NSArrayObjectAtIndex(xmlTags, i), 0)];
+ [xmlString appendString:[xmlItem HTMLEscapeString]];
+ [xmlString appendString:NSArrayObjectAtIndex(NSArrayObjectAtIndex(xmlTags, i), 1)];
+ }
}
-
- [xmlString appendFormat:@"\t\t<field name=\"%@\"", [[NSArrayObjectAtIndex(fieldNames, i) description] HTMLEscapeString]];
- if (dataIsNULL) {
- [xmlString appendString:@" xsi:nil=\"true\" \\>\n"];
- }
- else {
- [xmlString appendFormat:@">%@</field>\n", [xmlItem HTMLEscapeString]];
- }
- }
-
- [xmlString appendString:@"\t</row>\n\n"];
+ [xmlString appendString:@"\t</row>\n\n"];
+
+ // Record the total length for use with pool flushing
+ currentPoolDataLength += [xmlString length];
+
+ // Write the row to the filehandle
+ [[self exportOutputFile] writeData:[xmlString dataUsingEncoding:[self exportOutputEncoding]]];
+
+ // Update the progress counter and progress bar
+ currentRowIndex++;
+
+ // Update the progress
+ if (totalRows && (currentRowIndex * ([self exportMaxProgress] / totalRows)) > lastProgressValue) {
- // Record the total length for use with pool flushing
- currentPoolDataLength += [xmlString length];
-
- // Write the row to the filehandle
- [[self exportOutputFile] writeData:[xmlString dataUsingEncoding:[self exportOutputEncoding]]];
-
- // Update the progress counter and progress bar
- currentRowIndex++;
-
- // Update the progress
- if (totalRows && (currentRowIndex * ([self exportMaxProgress] / totalRows)) > lastProgressValue) {
+ NSInteger progress = (currentRowIndex * ([self exportMaxProgress] / totalRows));
+
+ [self setExportProgressValue:progress];
+
+ lastProgressValue = progress;
+ }
- NSInteger progress = (currentRowIndex * ([self exportMaxProgress] / totalRows));
+ // Inform the delegate that the export's progress has been updated
+ [delegate performSelectorOnMainThread:@selector(xmlExportProcessProgressUpdated:) withObject:self waitUntilDone:NO];
- [self setExportProgressValue:progress];
-
- lastProgressValue = progress;
+ // Drain the autorelease pool as required to keep memory usage low
+ if (currentPoolDataLength > 250000) {
+ [xmlExportPool release];
+ xmlExportPool = [[NSAutoreleasePool alloc] init];
+ }
+
+ // If an array was supplied and we've processed all rows, break
+ if ([self xmlDataArray] && totalRows == currentRowIndex) break;
}
- // Inform the delegate that the export's progress has been updated
- [delegate performSelectorOnMainThread:@selector(xmlExportProcessProgressUpdated:) withObject:self waitUntilDone:NO];
-
- // Drain the autorelease pool as required to keep memory usage low
- if (currentPoolDataLength > 250000) {
- [xmlExportPool release];
- xmlExportPool = [[NSAutoreleasePool alloc] init];
+ if (([self xmlFormat] == SPXMLExportMySQLFormat) && isTableExport) {
+ [[self exportOutputFile] writeData:[@"\t</table_data>\n\n" dataUsingEncoding:[self exportOutputEncoding]]];
+ }
+ else if ([self xmlFormat] == SPXMLExportPlainFormat) {
+ [[self exportOutputFile] writeData:[[NSString stringWithFormat:@"\t</%@>\n\n", ([self xmlTableName]) ? [[self xmlTableName] HTMLEscapeString] : @"custom"] dataUsingEncoding:[self exportOutputEncoding]]];
}
- // If an array was supplied and we've processed all rows, break
- if ([self xmlDataArray] && totalRows == currentRowIndex) break;
- }
-
- if ((![self xmlDataArray]) && [self xmlTableName]) {
- [[self exportOutputFile] writeData:[@"\t</table_data>\n\n" dataUsingEncoding:[self exportOutputEncoding]]];
+ [xmlExportPool release];
}
// Write data to disk
@@ -248,7 +347,6 @@
// Inform the delegate that the export process is complete
[delegate performSelectorOnMainThread:@selector(xmlExportProcessComplete:) withObject:self waitUntilDone:NO];
- [xmlExportPool release];
[pool release];
}
@@ -259,8 +357,7 @@
{
if (xmlDataArray) [xmlDataArray release], xmlDataArray = nil;
if (xmlTableName) [xmlTableName release], xmlTableName = nil;
-
- [xmlNULLString release], xmlNULLString = nil;
+ if (xmlNULLString) [xmlNULLString release], xmlNULLString = nil;
[super dealloc];
}