@@ -431,9 +431,25 @@ - (void)importSQLFile:(NSString *)filename
431431 sqlEncoding = [importEncodingPopup selectedTag ];
432432 }
433433
434+ // store the sqlMode to restore, if the import changes it
435+ NSString *sqlModeToRestore = nil ;
436+ {
437+ // this query should work in ≥ 4.1.0 (which is also the first version that allows setting sql_mode at runtime)
438+ SPMySQLResult *res = [mySQLConnection queryString: @" SELECT @@sql_mode" ];
439+ [res setReturnDataAsStrings: YES ]; // TODO #2700: The framework misinterprets binary collation as binary data, so in order to be safe force it to use strings
440+
441+ sqlModeToRestore = [[res getRowAsArray ] objectAtIndex: 0 ];
442+ }
443+
444+ SPMySQLServerStatusBits serverStatus;
445+ // initialize
446+ serverStatus.noBackslashEscapes = 0 ; // for the moment we only care about that flag
447+
434448 // Read in the file in a loop
435449 sqlParser = [[SPSQLParser alloc ] init ];
436450 [sqlParser setDelimiterSupport: YES ];
451+ [mySQLConnection updateServerStatusBits: &serverStatus];
452+ [sqlParser setNoBackslashEscapes: serverStatus.noBackslashEscapes];
437453 sqlDataBuffer = [[NSMutableData alloc ] init ];
438454 importPool = [[NSAutoreleasePool alloc ] init ];
439455 while (1 ) {
@@ -442,12 +458,14 @@ - (void)importSQLFile:(NSString *)filename
442458 @try {
443459 fileChunk = [sqlFileHandle readDataOfLength: fileChunkMaxLength];
444460 }
445-
446461 // Report file read errors, and bail
447462 @catch (NSException *exception) {
448463 if (connectionEncodingToRestore) {
449464 [mySQLConnection queryString: [NSString stringWithFormat: @" SET NAMES '%@ '" , connectionEncodingToRestore]];
450465 }
466+ if (sqlModeToRestore) {
467+ [mySQLConnection queryString: [NSString stringWithFormat: @" SET SQL_MODE=%@ " , [sqlModeToRestore tickQuotedString ]]];
468+ }
451469 [self closeAndStopProgressSheet ];
452470 SPOnewayAlertSheet (
453471 SP_FILE_READ_ERROR_STRING,
@@ -458,8 +476,7 @@ - (void)importSQLFile:(NSString *)filename
458476 [sqlDataBuffer release ];
459477 [importPool drain ];
460478 [tableDocumentInstance setQueryMode: SPInterfaceQueryMode];
461- if ([filename hasPrefix: SPImportClipboardTempFileNamePrefix])
462- [[NSFileManager defaultManager ] removeItemAtPath: filename error: nil ];
479+ if ([filename hasPrefix: SPImportClipboardTempFileNamePrefix]) [[NSFileManager defaultManager ] removeItemAtPath: filename error: nil ];
463480 return ;
464481 }
465482
@@ -488,11 +505,14 @@ - (void)importSQLFile:(NSString *)filename
488505
489506 // Try to generate a NSString with the resulting data
490507 sqlString = [[NSString alloc ] initWithData: [sqlDataBuffer subdataWithRange: NSMakeRange (dataBufferLastQueryEndPosition, dataBufferPosition - dataBufferLastQueryEndPosition)]
491- encoding: sqlEncoding];
508+ encoding: sqlEncoding];
492509 if (!sqlString) {
493510 if (connectionEncodingToRestore) {
494511 [mySQLConnection queryString: [NSString stringWithFormat: @" SET NAMES '%@ '" , connectionEncodingToRestore]];
495512 }
513+ if (sqlModeToRestore) {
514+ [mySQLConnection queryString: [NSString stringWithFormat: @" SET SQL_MODE=%@ " , [sqlModeToRestore tickQuotedString ]]];
515+ }
496516 [self closeAndStopProgressSheet ];
497517 NSString *displayEncoding;
498518 if (![importEncodingPopup indexOfSelectedItem ]) {
@@ -509,8 +529,7 @@ - (void)importSQLFile:(NSString *)filename
509529 [sqlDataBuffer release ];
510530 [importPool drain ];
511531 [tableDocumentInstance setQueryMode: SPInterfaceQueryMode];
512- if ([filename hasPrefix: SPImportClipboardTempFileNamePrefix])
513- [[NSFileManager defaultManager ] removeItemAtPath: filename error: nil ];
532+ if ([filename hasPrefix: SPImportClipboardTempFileNamePrefix]) [[NSFileManager defaultManager ] removeItemAtPath: filename error: nil ];
514533 return ;
515534 }
516535
@@ -531,12 +550,11 @@ - (void)importSQLFile:(NSString *)filename
531550 dataBufferPosition -= dataBufferLastQueryEndPosition;
532551 dataBufferLastQueryEndPosition = 0 ;
533552 }
534-
553+
535554 // Before entering the following loop, check that we actually have a connection.
536555 // If not, check the connection if appropriate and then clean up and exit if appropriate.
537556 if (![mySQLConnection isConnected ] && ([mySQLConnection userTriggeredDisconnect ] || ![mySQLConnection checkConnection ])) {
538- if ([filename hasPrefix: SPImportClipboardTempFileNamePrefix])
539- [[NSFileManager defaultManager ] removeItemAtPath: filename error: nil ];
557+ if ([filename hasPrefix: SPImportClipboardTempFileNamePrefix]) [[NSFileManager defaultManager ] removeItemAtPath: filename error: nil ];
540558 [self closeAndStopProgressSheet ];
541559 [errors appendString: NSLocalizedString(@" The connection to the server was lost during the import. The import is only partially complete." , @" Connection lost during import error message" )];
542560 [self showErrorSheetWithMessage: errors];
@@ -560,30 +578,33 @@ - (void)importSQLFile:(NSString *)filename
560578
561579 // Skip blank or whitespace-only queries to avoid errors
562580 if (![query length ]) continue ;
563-
581+
564582 // Run the query
565583 [mySQLConnection queryString: query usingEncoding: sqlEncoding withResultType: SPMySQLResultAsResult];
566584
585+ // in case the query was a "SET @@sql_mode = ...", the server_status may have changed
586+ if ([mySQLConnection updateServerStatusBits: &serverStatus]) [sqlParser setNoBackslashEscapes: serverStatus.noBackslashEscapes];
587+
567588 // Check for any errors
568589 if ([mySQLConnection queryErrored ] && ![[mySQLConnection lastErrorMessage ] isEqualToString: @" Query was empty" ]) {
569590 [errors appendFormat: NSLocalizedString(@" [ERROR in query %ld ] %@ \n " , @" error text when multiple custom query failed" ), (long )(queriesPerformed+1 ), [mySQLConnection lastErrorMessage ]];
570591
571592 // if the error is about utf8mb4 not being supported by the server display a more helpful message.
572593 // Note: the same error will occur when doing CREATE TABLE... with utf8mb4.
573- if ([mySQLConnection lastErrorID ] == 1115 && [[mySQLConnection lastErrorMessage ] rangeOfString: @" utf8mb4" options: NSCaseInsensitiveSearch].location != NSNotFound && [query rangeOfString: @" SET NAMES" options: NSCaseInsensitiveSearch].location != NSNotFound ) {
594+ if ([mySQLConnection lastErrorID ] == 1115 /* ER_UNKNOWN_CHARACTER_SET */ && [[mySQLConnection lastErrorMessage ] rangeOfString: @" utf8mb4" options: NSCaseInsensitiveSearch].location != NSNotFound && [query rangeOfString: @" SET NAMES" options: NSCaseInsensitiveSearch].location != NSNotFound ) {
574595 if (!ignoreCharsetError) {
575596 __block NSInteger charsetErrorSheetReturnCode;
576-
597+
577598 SPMainQSync (^{
578599 NSAlert *charsetErrorAlert = [NSAlert alertWithMessageText: NSLocalizedString(@" Incompatible encoding in SQL file" , @" sql import error message" )
579600 defaultButton: NSLocalizedString(@" Import Anyway" , @" sql import : charset error alert : continue button" )
580601 alternateButton: NSLocalizedString(@" Cancel Import" , @" sql import : charset error alert : cancel button" )
581- otherButton: nil
602+ otherButton: nil
582603 informativeTextWithFormat: NSLocalizedString(@" The SQL file uses utf8mb4 encoding, but your MySQL version only supports the limited utf8 subset.\n\n You can continue the import, but any non-BMP characters in the SQL file (eg. some typographic and scientific special characters, archaic CJK logograms, emojis) will be unrecoverably lost!" , @" sql import : charset error alert : detail message" )];
583604 [charsetErrorAlert setAlertStyle: NSWarningAlertStyle ];
584605 charsetErrorSheetReturnCode = [charsetErrorAlert runModal ];
585606 });
586-
607+
587608 switch (charsetErrorSheetReturnCode) {
588609 // don't display the message a second time
589610 case NSAlertDefaultReturn :
@@ -603,25 +624,22 @@ - (void)importSQLFile:(NSString *)filename
603624
604625 SPMainQSync (^{
605626 NSAlert *sqlErrorAlert = [NSAlert alertWithMessageText: NSLocalizedString(@" An error occurred while importing SQL" , @" sql import error message" )
606- defaultButton: NSLocalizedString(@" Continue" , @" continue button" )
607- alternateButton: NSLocalizedString(@" Ignore All Errors" , @" ignore errors button" )
608- otherButton: NSLocalizedString(@" Stop" , @" stop button" )
609- informativeTextWithFormat: NSLocalizedString(@" [ERROR in query %ld ] %@ \n " , @" error text when multiple custom query failed" ), (long )(queriesPerformed+1 ), [mySQLConnection lastErrorMessage ]];
627+ defaultButton: NSLocalizedString(@" Continue" , @" continue button" )
628+ alternateButton: NSLocalizedString(@" Ignore All Errors" , @" ignore errors button" )
629+ otherButton: NSLocalizedString(@" Stop" , @" stop button" )
630+ informativeTextWithFormat: NSLocalizedString(@" [ERROR in query %ld ] %@ \n " , @" error text when multiple custom query failed" ), (long )(queriesPerformed+1 ), [mySQLConnection lastErrorMessage ]];
610631 [sqlErrorAlert setAlertStyle: NSWarningAlertStyle ];
611632 sqlImportErrorSheetReturnCode = [sqlErrorAlert runModal ];
612633 });
613-
634+
614635 switch (sqlImportErrorSheetReturnCode) {
615-
616636 // On "continue", no additional action is required
617637 case NSAlertDefaultReturn :
618638 break ;
619-
620639 // Ignore all future errors if asked to
621640 case NSAlertAlternateReturn :
622641 ignoreSQLErrors = YES ;
623642 break ;
624-
625643 // Otherwise, stop
626644 default :
627645 [errors appendString: NSLocalizedString(@" Import cancelled!\n " , @" import cancelled message" )];
@@ -637,14 +655,14 @@ - (void)importSQLFile:(NSString *)filename
637655 if (fileIsCompressed) {
638656 [singleProgressBar setDoubleValue: [sqlFileHandle realDataReadLength ]];
639657 [singleProgressText setStringValue: [NSString stringWithFormat: NSLocalizedString(@" Imported %@ of SQL" , @" SQL import progress text where total size is unknown" ),
640- [NSString stringForByteSize: fileProcessedLength]]];
658+ [NSString stringForByteSize: fileProcessedLength]]];
641659 } else {
642660 [singleProgressBar setDoubleValue: fileProcessedLength];
643661 [singleProgressText setStringValue: [NSString stringWithFormat: NSLocalizedString(@" Imported %@ of %@ " , @" SQL import progress text" ),
644662 [NSString stringForByteSize: fileProcessedLength], [NSString stringForByteSize: fileTotalLength]]];
645663 }
646664 }
647-
665+
648666 // If all the data has been read, break out of the processing loop
649667 if (allDataRead) break ;
650668
@@ -659,6 +677,7 @@ - (void)importSQLFile:(NSString *)filename
659677
660678 // Run the query
661679 [mySQLConnection queryString: query usingEncoding: sqlEncoding withResultType: SPMySQLResultAsResult];
680+ // we don't care for the server_status that is set AFTER the last query has been executed
662681
663682 // Check for any errors
664683 if ([mySQLConnection queryErrored ] && ![[mySQLConnection lastErrorMessage ] isEqualToString: @" Query was empty" ]) {
@@ -673,12 +692,14 @@ - (void)importSQLFile:(NSString *)filename
673692 if (connectionEncodingToRestore) {
674693 [mySQLConnection queryString: [NSString stringWithFormat: @" SET NAMES '%@ '" , connectionEncodingToRestore]];
675694 }
695+ if (sqlModeToRestore) {
696+ [mySQLConnection queryString: [NSString stringWithFormat: @" SET SQL_MODE=%@ " , [sqlModeToRestore tickQuotedString ]]];
697+ }
676698 [sqlParser release ];
677699 [sqlDataBuffer release ];
678700 [importPool drain ];
679701 [tableDocumentInstance setQueryMode: SPInterfaceQueryMode];
680- if ([filename hasPrefix: SPImportClipboardTempFileNamePrefix])
681- [[NSFileManager defaultManager ] removeItemAtPath: filename error: nil ];
702+ if ([filename hasPrefix: SPImportClipboardTempFileNamePrefix]) [[NSFileManager defaultManager ] removeItemAtPath: filename error: nil ];
682703
683704 // Close progress sheet
684705 [self closeAndStopProgressSheet ];
0 commit comments