aboutsummaryrefslogtreecommitdiffstats
path: root/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Max Packet Size.m
diff options
context:
space:
mode:
Diffstat (limited to 'Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Max Packet Size.m')
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Max Packet Size.m196
1 files changed, 196 insertions, 0 deletions
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Max Packet Size.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Max Packet Size.m
new file mode 100644
index 00000000..e0bfef52
--- /dev/null
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Max Packet Size.m
@@ -0,0 +1,196 @@
+//
+// $Id$
+//
+// Max Packet Size.m
+// SPMySQLFramework
+//
+// Created by Rowan Beentje (rowan.beent.je) on February 9, 2012
+// Copyright (c) 2012 Rowan Beentje. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <http://code.google.com/p/sequel-pro/>
+
+
+#import "Max Packet Size.h"
+#import "SPMySQL Private APIs.h"
+
+@implementation SPMySQLConnection (Max_Packet_Size)
+
+/**
+ * Retrieve the current maximum query size (MySQL's max_allowed_packet), as cached
+ * by the class. If the connection has been unable to retrieve this value, the
+ * default of 1MB will be returned.
+ */
+- (NSUInteger)maxQuerySize
+{
+ return maxQuerySize;
+}
+
+/**
+ * Retrieve whether the server's maximum query size (MySQL's max_allowed_packet) is
+ * editable by the current user.
+ */
+- (BOOL)isMaxQuerySizeEditable
+{
+ if (!maxQuerySizeEditabilityChecked) {
+ [self _updateMaxQuerySizeEditability];
+ }
+
+ return maxQuerySizeIsEditable;
+}
+
+/**
+ * Set the servers's global maximum query size - MySQL's max_allowed_packed - to the
+ * supplied size. Note that this *does not* affect the current connection; a reconnection
+ * is required to pick up the new size setting. As a result it may be important to restore
+ * the connection size after use.
+ * Validates the supplied size (eg 1GB limit) and applies it if appropriate, returning
+ * the set query size or NSNotFound on error.
+ */
+- (NSUInteger)setGlobalMaxQuerySize:(NSUInteger)newMaxSize
+{
+
+ // Perform basic validation. First, ensure the max query size is editable
+ if (![self isMaxQuerySizeEditable]) return NSNotFound;
+
+ // Validate sizes
+ if (newMaxSize < 1024) return NSNotFound;
+ if (newMaxSize > (1024 * 1024 * 1024)) newMaxSize = 1024 * 1024 * 1024;
+
+ // Perform a standard query to set the new size
+ [self queryString:[NSString stringWithFormat:@"SET GLOBAL max_allowed_packet = %lu", newMaxSize]];
+
+ // On failure, return NSNotFound - error state will have automatically been set
+ if ([self queryErrored]) return NSNotFound;
+
+ // Otherwise, set the local instance variable and return success
+ maxQuerySize = newMaxSize;
+ return maxQuerySize;
+}
+
+@end
+
+#pragma mark -
+
+@implementation SPMySQLConnection (Max_Packet_Size_Private_API)
+
+/**
+ * Update the max_allowed_packet size - the largest supported query size - from the server.
+ */
+- (void)_updateMaxQuerySize
+{
+
+ // Determine which query to run based on server version
+ NSString *packetQueryString;
+ if ([self serverMajorVersion] == 3) {
+ packetQueryString = @"SHOW VARIABLES LIKE 'max_allowed_packet'";
+ } else {
+ packetQueryString = @"SELECT @@global.max_allowed_packet";
+ }
+
+ // Make a standard query to the server to retrieve the information
+ SPMySQLResult *result = [self queryString:packetQueryString];
+ [result setReturnDataAsStrings:YES];
+
+ // Get the maximum size string
+ NSString *maxQuerySizeString = nil;
+ if ([self serverMajorVersion] == 3) {
+ maxQuerySizeString = [[result getRowAsArray] objectAtIndex:1];
+ } else {
+ maxQuerySizeString = [[result getRowAsArray] objectAtIndex:0];
+ }
+
+ // If a valid size was returned, update the instance variable
+ if (maxQuerySizeString) {
+ maxQuerySize = (NSUInteger)[maxQuerySizeString integerValue];
+ }
+}
+
+/**
+ * Perform a query to determine whether the current user has permission to edit the
+ * max_allowed_packet setting for their connection.
+ */
+- (void)_updateMaxQuerySizeEditability
+{
+ [self queryString:@"SET GLOBAL max_allowed_packet = @@global.max_allowed_packet"];
+ maxQuerySizeIsEditable = ![self queryErrored];
+ maxQuerySizeEditabilityChecked = YES;
+}
+
+/**
+ * Attempts to change the maximum query size in order to allow a query to be performed.
+ * Returns whether the change was successfully made.
+ */
+- (BOOL)_attemptMaxQuerySizeIncreaseTo:(NSUInteger)targetSize
+{
+
+ // If the query size is editable, attempt to increase the size
+ if ([self isMaxQuerySizeEditable]) {
+ NSUInteger newSize = [self setGlobalMaxQuerySize:targetSize];
+ if (newSize != NSNotFound) {
+
+ // Successfully increased the global size - reconnect to use it, and return success
+ [self reconnect];
+ return YES;
+ }
+ }
+
+ // Can not, or failed to, increase the max query size. Record an error message.
+ NSString *errorMessage = [NSString stringWithFormat:NSLocalizedString(@"The query length of %lu bytes is larger than max_allowed_packet size (%lu).", @"error message if max_allowed_packet < query size"), targetSize, maxQuerySize];
+ [self _updateLastErrorMessage:errorMessage];
+
+ // Update delegate error if it supports the protocol
+ if ([delegate respondsToSelector:@selector(queryGaveError:connection:)]) {
+ [delegate queryGaveError:errorMessage connection:self];
+ }
+
+ // Display an alert as this is a special failure
+ if ([delegate respondsToSelector:@selector(showErrorWithTitle:message:)]) {
+ [delegate showErrorWithTitle:NSLocalizedString(@"Error", @"error") message:errorMessage];
+ } else {
+ NSRunAlertPanel(NSLocalizedString(@"Error", @"error"), errorMessage, @"OK", nil, nil);
+ }
+
+ return NO;
+}
+
+/**
+ * Restore a maximum query size after temporarily increasing it for a query. This action
+ * may be called directly after a query, or may be before the next query if a streaming result
+ * had to be used.
+ */
+- (void)_restoreMaximumQuerySizeAfterQuery
+{
+
+ // Return if no action needs to be performed
+ if (queryActionShouldRestoreMaxQuerySize == NSNotFound) return;
+
+ // Move the target size to a local variable to prevent looping
+ NSUInteger targetMaxQuerySize = queryActionShouldRestoreMaxQuerySize;
+ queryActionShouldRestoreMaxQuerySize = NSNotFound;
+
+ // Enact the change
+ [self setGlobalMaxQuerySize:targetMaxQuerySize];
+}
+
+@end