aboutsummaryrefslogtreecommitdiffstats
path: root/Frameworks/SPMySQLFramework
diff options
context:
space:
mode:
Diffstat (limited to 'Frameworks/SPMySQLFramework')
-rw-r--r--Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql_com.h6
-rw-r--r--Frameworks/SPMySQLFramework/SPMySQL Unit Tests/DataConversion_Tests.m77
-rw-r--r--Frameworks/SPMySQLFramework/SPMySQL Unit Tests/Info.plist24
-rw-r--r--Frameworks/SPMySQLFramework/SPMySQL Unit Tests/SPMySQLStringAdditions_Tests.m89
-rw-r--r--Frameworks/SPMySQLFramework/SPMySQLDataTypes.h1
-rw-r--r--Frameworks/SPMySQLFramework/SPMySQLDataTypes.m1
-rw-r--r--Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj294
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h8
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Encoding.m6
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h6
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m60
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m29
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.h10
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m76
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h9
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m90
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLFastStreamingResult.m4
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.h7
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m49
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m14
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLResult.m2
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLStreamingResultStore.m4
22 files changed, 709 insertions, 157 deletions
diff --git a/Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql_com.h b/Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql_com.h
index f2345be6..8a8c019d 100644
--- a/Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql_com.h
+++ b/Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql_com.h
@@ -348,7 +348,11 @@ enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY,
MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR,
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
MYSQL_TYPE_BIT,
- MYSQL_TYPE_NEWDECIMAL=246,
+ MYSQL_TYPE_TIMESTAMP2,
+ MYSQL_TYPE_DATETIME2,
+ MYSQL_TYPE_TIME2,
+ MYSQL_TYPE_JSON=245,
+ MYSQL_TYPE_NEWDECIMAL=246,
MYSQL_TYPE_ENUM=247,
MYSQL_TYPE_SET=248,
MYSQL_TYPE_TINY_BLOB=249,
diff --git a/Frameworks/SPMySQLFramework/SPMySQL Unit Tests/DataConversion_Tests.m b/Frameworks/SPMySQLFramework/SPMySQL Unit Tests/DataConversion_Tests.m
new file mode 100644
index 00000000..b8256a5c
--- /dev/null
+++ b/Frameworks/SPMySQLFramework/SPMySQL Unit Tests/DataConversion_Tests.m
@@ -0,0 +1,77 @@
+//
+// DataConversion_Tests.m
+// SPMySQLFramework
+//
+// Created by Max Lohrmann on 01.10.15.
+// Copyright (c) 2015 Max Lohrmann. 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 <https://github.com/sequelpro/sequelpro>
+
+#import <Cocoa/Cocoa.h>
+#import <XCTest/XCTest.h>
+
+// this function is inaccessible outside of unit tests
+extern NSString * _bitStringWithBytes(const char *bytes, NSUInteger length, NSUInteger padLength);
+
+@interface DataConversion_Tests : XCTestCase
+
+- (void)test_bitStringWithBytes;
+
+@end
+
+@implementation DataConversion_Tests
+
+- (void)test_bitStringWithBytes
+{
+ // BIT(1)
+ {
+ const char y = '\1';
+ const char n = '\0';
+ XCTAssertEqualObjects(_bitStringWithBytes(&y,sizeof(y),1), @"1");
+ XCTAssertEqualObjects(_bitStringWithBytes(&n,sizeof(n),1), @"0");
+ }
+ // BIT(3)
+ {
+ const char input[] = {5};
+ NSUInteger bitSize = 3;
+ NSString *res = _bitStringWithBytes(input,sizeof(input),bitSize);
+ XCTAssertEqualObjects(res, @"101");
+ }
+ // BIT(16)
+ {
+ const char input[] = {0xcc,0xf0};
+ NSUInteger bitSize = 16;
+ NSString *res = _bitStringWithBytes(input,sizeof(input),bitSize);
+ XCTAssertEqualObjects(res, @"1100110011110000");
+ }
+ // BIT(20)
+ {
+ const char input[] = {0x0f,0xcc,0xf0};
+ NSUInteger bitSize = 20;
+ NSString *res = _bitStringWithBytes(input,sizeof(input),bitSize);
+ XCTAssertEqualObjects(res, @"11111100110011110000");
+ }
+}
+
+@end
diff --git a/Frameworks/SPMySQLFramework/SPMySQL Unit Tests/Info.plist b/Frameworks/SPMySQLFramework/SPMySQL Unit Tests/Info.plist
new file mode 100644
index 00000000..38544a84
--- /dev/null
+++ b/Frameworks/SPMySQLFramework/SPMySQL Unit Tests/Info.plist
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.sequelpro.spmysql-unittests</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/Frameworks/SPMySQLFramework/SPMySQL Unit Tests/SPMySQLStringAdditions_Tests.m b/Frameworks/SPMySQLFramework/SPMySQL Unit Tests/SPMySQLStringAdditions_Tests.m
new file mode 100644
index 00000000..cc1f44dd
--- /dev/null
+++ b/Frameworks/SPMySQLFramework/SPMySQL Unit Tests/SPMySQLStringAdditions_Tests.m
@@ -0,0 +1,89 @@
+//
+// SPMySQLStringAdditions_Tests.m
+// SPMySQLFramework
+//
+// Created by Max Lohrmann on 04.10.15.
+//
+//
+
+#import <Cocoa/Cocoa.h>
+#import <XCTest/XCTest.h>
+#import <SPMySQL/SPMySQL.h>
+
+@interface SPMySQLStringAdditions_Tests : XCTestCase
+
+- (void)test_mySQLBacktickQuotedString;
+- (void)test_mySQLTickQuotedString;
+- (void)test_stringForDataBytesLengthEncoding;
+
+@end
+
+@implementation SPMySQLStringAdditions_Tests
+
+- (void)test_mySQLBacktickQuotedString
+{
+ XCTAssertEqualObjects([@"" mySQLBacktickQuotedString], @"``",@"empty string");
+
+ XCTAssertEqualObjects([@"tbl1" mySQLBacktickQuotedString], @"`tbl1`", @"regular string");
+
+ XCTAssertEqualObjects([@"tbl`1" mySQLBacktickQuotedString], @"`tbl``1`",@"string with control character");
+
+ XCTAssertEqualObjects([@"tbl``" mySQLBacktickQuotedString], @"`tbl`````",@"string with escaped control character at end");
+}
+
+- (void)test_mySQLTickQuotedString
+{
+ XCTAssertEqualObjects([@"" mySQLTickQuotedString], @"''",@"empty string");
+
+ XCTAssertEqualObjects([@"tbl1" mySQLTickQuotedString], @"'tbl1'", @"regular string");
+
+ XCTAssertEqualObjects([@"tbl'1" mySQLTickQuotedString], @"'tbl''1'",@"string with control character");
+
+ XCTAssertEqualObjects([@"tbl''" mySQLTickQuotedString], @"'tbl'''''",@"string with escaped control character at end");
+}
+
+- (void)test_stringForDataBytesLengthEncoding
+{
+ {
+ const char chr = '\0';
+ NSString *conv = [NSString stringForDataBytes:&chr length:0 encoding:NSISOLatin1StringEncoding];
+ XCTAssertEqualObjects(conv, @"",@"empty string test");
+ }
+ {
+ const char *cstr = "an ASCII C string";
+ NSString *conv = [NSString stringForDataBytes:cstr length:strlen(cstr) encoding:NSASCIIStringEncoding];
+ XCTAssertEqualObjects(conv, @"an ASCII C string", @"simple ASCII string test");
+ }
+ {
+ // the euro sign is the tricky part
+ // ISO-8859-1 (aka Latin1): not supported, codepoint 0x80 is not in use
+ // ISO-8859-1 + ISO/IEC 6429: not supported, codepoint 0x80 is PAD control character
+ // ISO-8859-15 (aka Latin9): € is at 0xA4, codepoint 0x80 is PAD control character
+ // Windows cp1252 (aka latin1 in mysql): € is at 0x80, codepoint 0xA4 is "¤"
+ const char cstr[] = {'\xE4','-','\xDF','-','\x80','\0'};
+ NSString *conv = [NSString stringForDataBytes:cstr length:strlen(cstr) encoding:NSWindowsCP1252StringEncoding];
+ XCTAssertEqualObjects(conv, @"ä-ß-€",@"handling of cp1252 special characters");
+
+ unsigned char latin9 = 0xA4;
+ NSString *conv2 = [NSString stringForDataBytes:&latin9 length:1 encoding:CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatin9)];
+ XCTAssertEqualObjects(conv2, @"€",@"handling of iso-8859-15 special characters");
+ }
+ {
+ const char *cstr = "エスキューエル";
+ NSString *conv = [NSString stringForDataBytes:cstr length:strlen(cstr) encoding:NSUTF8StringEncoding];
+ XCTAssertEqualObjects(conv, @"エスキューエル",@"handling of valid utf-8 string");
+ }
+ {
+ // this is a test for a certain mysql issue:
+ // mysql limits field names to 255 characters and will even cut multibyte chars in the middle,
+ // if neccesary. This will create invalid characters which cause NSString
+ // to fail and return nil on the whole string. Since we know that, we can
+ // at least try to return something.
+ char cstr[] = {'\xE3','\x82','\xA8','\xE3','\x82','\xB9','\xE3','\x82','\xAD','\xE3','\x83','\xA5','\xE3','\x83','\xBC','\xE3','\x82','\xA8','\xE3','\x83','\xAB','\0'}; // エスキューエル
+ cstr[strlen(cstr)-2] = '\0'; //simulate cutting off the string
+ NSString *conv = [NSString stringForDataBytes:cstr length:strlen(cstr) encoding:NSUTF8StringEncoding];
+ XCTAssertNotNil(conv, @"handling of invalid utf8 sequences");
+ }
+}
+
+@end
diff --git a/Frameworks/SPMySQLFramework/SPMySQLDataTypes.h b/Frameworks/SPMySQLFramework/SPMySQLDataTypes.h
index def1e988..31acf6b8 100644
--- a/Frameworks/SPMySQLFramework/SPMySQLDataTypes.h
+++ b/Frameworks/SPMySQLFramework/SPMySQLDataTypes.h
@@ -72,3 +72,4 @@ extern NSString *SPMySQLMultiPointType;
extern NSString *SPMySQLMultiLineStringType;
extern NSString *SPMySQLMultiPolygonType;
extern NSString *SPMySQLGeometryCollectionType;
+extern NSString *SPMySQLJsonType;
diff --git a/Frameworks/SPMySQLFramework/SPMySQLDataTypes.m b/Frameworks/SPMySQLFramework/SPMySQLDataTypes.m
index 06708f1b..d7f54c0e 100644
--- a/Frameworks/SPMySQLFramework/SPMySQLDataTypes.m
+++ b/Frameworks/SPMySQLFramework/SPMySQLDataTypes.m
@@ -74,3 +74,4 @@ NSString *SPMySQLMultiPointType = @"MULTIPOINT";
NSString *SPMySQLMultiLineStringType = @"MULTILINESTRING";
NSString *SPMySQLMultiPolygonType = @"MULTIPOLYGON";
NSString *SPMySQLGeometryCollectionType = @"GEOMETRYCOLLECTION";
+NSString *SPMySQLJsonType = @"JSON";
diff --git a/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj b/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj
index 9acf9417..32dca24c 100644
--- a/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj
+++ b/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj
@@ -9,6 +9,9 @@
/* Begin PBXBuildFile section */
17E3A57B1885A286009CF372 /* SPMySQLDataTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 17E3A5791885A286009CF372 /* SPMySQLDataTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
17E3A57C1885A286009CF372 /* SPMySQLDataTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E3A57A1885A286009CF372 /* SPMySQLDataTypes.m */; };
+ 507FF1E51BC0D82300104523 /* DataConversion_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 507FF1811BC0C64100104523 /* DataConversion_Tests.m */; };
+ 507FF23B1BC0E8CA00104523 /* SPMySQL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* SPMySQL.framework */; };
+ 507FF23D1BC157B500104523 /* SPMySQLStringAdditions_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 507FF23C1BC157B500104523 /* SPMySQLStringAdditions_Tests.m */; };
580A331E14D75CF7000D6933 /* SPMySQLGeometryData.h in Headers */ = {isa = PBXBuildFile; fileRef = 580A331C14D75CF7000D6933 /* SPMySQLGeometryData.h */; settings = {ATTRIBUTES = (Public, ); }; };
580A331F14D75CF7000D6933 /* SPMySQLGeometryData.m in Sources */ = {isa = PBXBuildFile; fileRef = 580A331D14D75CF7000D6933 /* SPMySQLGeometryData.m */; };
583C734A17A489CC0056B284 /* SPMySQLStreamingResultStoreDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 583C734917A489CC0056B284 /* SPMySQLStreamingResultStoreDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -75,6 +78,16 @@
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
/* End PBXBuildFile section */
+/* Begin PBXContainerItemProxy section */
+ 507FF2391BC0E8AF00104523 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 8DC2EF4F0486A6940098B216;
+ remoteInfo = SPMySQL.framework;
+ };
+/* End PBXContainerItemProxy section */
+
/* Begin PBXFileReference section */
0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
@@ -83,6 +96,10 @@
17E3A5791885A286009CF372 /* SPMySQLDataTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPMySQLDataTypes.h; sourceTree = "<group>"; };
17E3A57A1885A286009CF372 /* SPMySQLDataTypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPMySQLDataTypes.m; sourceTree = "<group>"; };
32DBCF5E0370ADEE00C91783 /* SPMySQLFramework_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SPMySQLFramework_Prefix.pch; path = Source/SPMySQLFramework_Prefix.pch; sourceTree = "<group>"; };
+ 507FF1811BC0C64100104523 /* DataConversion_Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DataConversion_Tests.m; sourceTree = "<group>"; };
+ 507FF1D51BC0D7D300104523 /* SPMySQL Unit Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SPMySQL Unit Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 507FF1D81BC0D7D300104523 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 507FF23C1BC157B500104523 /* SPMySQLStringAdditions_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPMySQLStringAdditions_Tests.m; sourceTree = "<group>"; };
580A331C14D75CF7000D6933 /* SPMySQLGeometryData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SPMySQLGeometryData.h; path = Source/SPMySQLGeometryData.h; sourceTree = "<group>"; };
580A331D14D75CF7000D6933 /* SPMySQLGeometryData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SPMySQLGeometryData.m; path = Source/SPMySQLGeometryData.m; sourceTree = "<group>"; };
583C734917A489CC0056B284 /* SPMySQLStreamingResultStoreDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SPMySQLStreamingResultStoreDelegate.h; path = Source/SPMySQLStreamingResultStoreDelegate.h; sourceTree = "<group>"; };
@@ -153,6 +170,14 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 507FF1D21BC0D7D300104523 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 507FF23B1BC0E8CA00104523 /* SPMySQL.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8DC2EF560486A6940098B216 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -171,6 +196,7 @@
isa = PBXGroup;
children = (
8DC2EF5B0486A6940098B216 /* SPMySQL.framework */,
+ 507FF1D51BC0D7D300104523 /* SPMySQL Unit Tests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -184,6 +210,7 @@
08FB77AEFE84172EC02AAC07 /* Classes */,
58C009D214E31D1300AC489A /* Category Additions */,
32C88DFF0371C24200C91783 /* Other Sources */,
+ 507FF1801BC0C64100104523 /* Unit Tests */,
089C1665FE841158C02AAC07 /* Resources */,
58428DF514BA5A03000F8438 /* Scripts */,
0867D69AFE84028FC02AAC07 /* Linked Frameworks */,
@@ -252,6 +279,17 @@
name = "Other Sources";
sourceTree = "<group>";
};
+ 507FF1801BC0C64100104523 /* Unit Tests */ = {
+ isa = PBXGroup;
+ children = (
+ 507FF1D81BC0D7D300104523 /* Info.plist */,
+ 507FF1811BC0C64100104523 /* DataConversion_Tests.m */,
+ 507FF23C1BC157B500104523 /* SPMySQLStringAdditions_Tests.m */,
+ );
+ name = "Unit Tests";
+ path = "SPMySQL Unit Tests";
+ sourceTree = "<group>";
+ };
580A331B14D75CCF000D6933 /* Result types */ = {
isa = PBXGroup;
children = (
@@ -419,6 +457,24 @@
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
+ 507FF1D41BC0D7D300104523 /* SPMySQL Unit Tests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 507FF1DE1BC0D7D300104523 /* Build configuration list for PBXNativeTarget "SPMySQL Unit Tests" */;
+ buildPhases = (
+ 507FF1D11BC0D7D300104523 /* Sources */,
+ 507FF1D21BC0D7D300104523 /* Frameworks */,
+ 507FF1D31BC0D7D300104523 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 507FF23A1BC0E8AF00104523 /* PBXTargetDependency */,
+ );
+ name = "SPMySQL Unit Tests";
+ productName = "SPMySQL Unit Tests";
+ productReference = 507FF1D51BC0D7D300104523 /* SPMySQL Unit Tests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
8DC2EF4F0486A6940098B216 /* SPMySQL.framework */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "SPMySQL.framework" */;
@@ -445,6 +501,11 @@
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0510;
+ TargetAttributes = {
+ 507FF1D41BC0D7D300104523 = {
+ CreatedOnToolsVersion = 6.2;
+ };
+ };
};
buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "SPMySQLFramework" */;
compatibilityVersion = "Xcode 3.2";
@@ -462,11 +523,19 @@
projectRoot = "";
targets = (
8DC2EF4F0486A6940098B216 /* SPMySQL.framework */,
+ 507FF1D41BC0D7D300104523 /* SPMySQL Unit Tests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 507FF1D31BC0D7D300104523 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8DC2EF520486A6940098B216 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -478,6 +547,15 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 507FF1D11BC0D7D300104523 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 507FF23D1BC157B500104523 /* SPMySQLStringAdditions_Tests.m in Sources */,
+ 507FF1E51BC0D82300104523 /* DataConversion_Tests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8DC2EF540486A6940098B216 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -510,6 +588,14 @@
};
/* End PBXSourcesBuildPhase section */
+/* Begin PBXTargetDependency section */
+ 507FF23A1BC0E8AF00104523 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 8DC2EF4F0486A6940098B216 /* SPMySQL.framework */;
+ targetProxy = 507FF2391BC0E8AF00104523 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
/* Begin PBXVariantGroup section */
089C1666FE841158C02AAC07 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
@@ -532,7 +618,6 @@
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_DYNAMIC_NO_PIC = NO;
- GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Source/SPMySQLFramework_Prefix.pch;
GENERATE_PKGINFO_FILE = YES;
@@ -559,7 +644,6 @@
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
- GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Source/SPMySQLFramework_Prefix.pch;
INFOPLIST_FILE = Resources/Info.plist;
@@ -644,6 +728,198 @@
};
name = Release;
};
+ 507FF1DF1BC0D7D300104523 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = NO;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ COMBINE_HIDPI_IMAGES = YES;
+ COPY_PHASE_STRIP = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(DEVELOPER_FRAMEWORKS_DIR)",
+ "$(inherited)",
+ );
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ INFOPLIST_FILE = "SPMySQL Unit Tests/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 507FF1E11BC0D7D300104523 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = NO;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ COMBINE_HIDPI_IMAGES = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(DEVELOPER_FRAMEWORKS_DIR)",
+ "$(inherited)",
+ );
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ INFOPLIST_FILE = "SPMySQL Unit Tests/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ 507FF1E21BC0D7D300104523 /* Distribution */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = NO;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ COMBINE_HIDPI_IMAGES = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(DEVELOPER_FRAMEWORKS_DIR)",
+ "$(inherited)",
+ );
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ INFOPLIST_FILE = "SPMySQL Unit Tests/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Distribution;
+ };
+ 507FF2361BC0E0A800104523 /* Unit Testing */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ CLANG_LINK_OBJC_RUNTIME = NO;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = "";
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
+ GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
+ GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
+ GCC_WARN_MISSING_PARENTHESES = YES;
+ GCC_WARN_SHADOW = YES;
+ GCC_WARN_SIGN_COMPARE = YES;
+ GCC_WARN_STRICT_SELECTOR_MATCH = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACH_O_TYPE = mh_dylib;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ VALID_ARCHS = "i386 x86_64";
+ };
+ name = "Unit Testing";
+ };
+ 507FF2371BC0E0A800104523 /* Unit Testing */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COMBINE_HIDPI_IMAGES = YES;
+ DEAD_CODE_STRIPPING = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = Source/SPMySQLFramework_Prefix.pch;
+ GCC_PREPROCESSOR_DEFINITIONS = SPMYSQL_FOR_UNIT_TESTING;
+ GENERATE_PKGINFO_FILE = YES;
+ INFOPLIST_FILE = Resources/Info.plist;
+ INSTALL_PATH = "@executable_path/../Frameworks";
+ LD_DYLIB_INSTALL_NAME = "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/MySQL Client Libraries/lib\"",
+ );
+ PRODUCT_NAME = SPMySQL;
+ SKIP_INSTALL = YES;
+ WRAPPER_EXTENSION = framework;
+ };
+ name = "Unit Testing";
+ };
+ 507FF2381BC0E0A800104523 /* Unit Testing */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = NO;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ COMBINE_HIDPI_IMAGES = YES;
+ COPY_PHASE_STRIP = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(DEVELOPER_FRAMEWORKS_DIR)",
+ "$(inherited)",
+ );
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ INFOPLIST_FILE = "SPMySQL Unit Tests/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = "Unit Testing";
+ };
586AA55214F5D599007F82BF /* Distribution */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -688,7 +964,6 @@
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
- GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Source/SPMySQLFramework_Prefix.pch;
INFOPLIST_FILE = Resources/Info.plist;
@@ -710,6 +985,7 @@
isa = XCConfigurationList;
buildConfigurations = (
1DEB91AE08733DA50010E9CD /* Debug */,
+ 507FF2371BC0E0A800104523 /* Unit Testing */,
1DEB91AF08733DA50010E9CD /* Release */,
586AA55314F5D599007F82BF /* Distribution */,
);
@@ -720,12 +996,24 @@
isa = XCConfigurationList;
buildConfigurations = (
1DEB91B208733DA50010E9CD /* Debug */,
+ 507FF2361BC0E0A800104523 /* Unit Testing */,
1DEB91B308733DA50010E9CD /* Release */,
586AA55214F5D599007F82BF /* Distribution */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 507FF1DE1BC0D7D300104523 /* Build configuration list for PBXNativeTarget "SPMySQL Unit Tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 507FF1DF1BC0D7D300104523 /* Debug */,
+ 507FF2381BC0E0A800104523 /* Unit Testing */,
+ 507FF1E11BC0D7D300104523 /* Release */,
+ 507FF1E21BC0D7D300104523 /* Distribution */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
/* End XCConfigurationList section */
};
rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
index 2419d7a9..1e2a8c14 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
@@ -82,6 +82,7 @@
@interface SPMySQLConnection (Querying_and_Preparation_Private_API)
- (void)_flushMultipleResultSets;
+- (void)_updateLastErrorInfos;
- (void)_updateLastErrorMessage:(NSString *)theErrorMessage;
- (void)_updateLastErrorID:(NSUInteger)theErrorID;
- (void)_updateLastSqlstate:(NSString *)theSqlstate;
@@ -99,12 +100,7 @@
@end
// SPMySQLResult Data Conversion Private API
-@interface SPMySQLResult (Data_Conversion_Private_API)
-
-+ (void)_initializeDataConversion;
-- (id)_getObjectFromBytes:(char *)bytes ofLength:(NSUInteger)length fieldDefinitionIndex:(NSUInteger)fieldIndex previewLength:(NSUInteger)previewLength;
-
-@end
+#import "Data Conversion.h"
/**
* Set up a static function to allow fast calling of SPMySQLResult data conversion with cached selectors
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Encoding.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Encoding.m
index fb949679..76f323bc 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Encoding.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Encoding.m
@@ -213,7 +213,7 @@
if (!strcmp(mysqlCharset, "utf8")) {
return NSUTF8StringEncoding;
} else if (!strcmp(mysqlCharset, "latin1")) {
- return NSISOLatin1StringEncoding;
+ return NSWindowsCP1252StringEncoding; // Warning: This is NOT the same as ISO-8859-1 (aka "ISO Latin 1")
} else if (!strcmp(mysqlCharset, "ascii")) {
return NSASCIIStringEncoding;
@@ -289,7 +289,7 @@
} else if (!strcmp(mysqlCharset, "dos")) {
return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingDOSLatin1);
} else if (!strcmp(mysqlCharset, "german1")) {
- return NSISOLatin1StringEncoding;
+ return NSWindowsCP1252StringEncoding;
} else if (!strcmp(mysqlCharset, "usa7")) {
return NSASCIIStringEncoding;
} else if (!strcmp(mysqlCharset, "danish")) {
@@ -313,7 +313,7 @@
} else if (!strcmp(mysqlCharset, "croat")) {
return NSISOLatin2StringEncoding;
} else if (!strcmp(mysqlCharset, "latin1_de")) {
- return NSISOLatin1StringEncoding;
+ return NSWindowsCP1252StringEncoding;
}
/**
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h
index cff8d43b..a3f34817 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h
@@ -32,8 +32,8 @@
typedef struct {
MYSQL *mySQLConnection;
- BOOL *keepAlivePingActivePointer;
- BOOL *keepAliveLastPingSuccessPointer;
+ volatile BOOL *keepAlivePingThreadActivePointer;
+ volatile BOOL *keepAliveLastPingSuccessPointer;
} SPMySQLConnectionPingDetails;
@interface SPMySQLConnection (Ping_and_KeepAlive)
@@ -51,6 +51,6 @@ void _forceThreadExit(int signalNumber);
void _pingThreadCleanup(void *pingDetails);
// Cancellation
-- (void)_cancelKeepAlives;
+- (BOOL)_cancelKeepAlives;
@end
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m
index e8338bb4..7940b483 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m
@@ -80,6 +80,10 @@
*/
- (void)_threadedKeepAlive
{
+ if(keepAliveThread) {
+ NSLog(@"warning: overwriting existing keepAliveThread: %@, results may be unpredictable!",keepAliveThread);
+ }
+
keepAliveThread = [NSThread currentThread];
[keepAliveThread setName:@"SPMySQL connection keepalive monitor thread"];
@@ -90,16 +94,15 @@
// attempt a single reconnection in the background
if (_elapsedSecondsSinceAbsoluteTime(lastConnectionUsedTime) < 60 * 15) {
[self _reconnectAfterBackgroundConnectionLoss];
-
+ }
// Otherwise set the state to connection lost for automatic reconnect on
// next use.
- } else {
+ else {
state = SPMySQLConnectionLostInBackground;
}
// Return as no further ping action required this cycle.
- keepAliveThread = nil;
- return;
+ goto end_cleanup;
}
// Otherwise, perform a background ping.
@@ -109,6 +112,7 @@
} else {
keepAlivePingFailures++;
}
+end_cleanup:
keepAliveThread = nil;
}
@@ -135,8 +139,13 @@
// Set up a query lock
[self _lockConnection];
+ //we might find ourselves at the losing end of a contest with -[self _disconnect]
+ if(!mySQLConnection) {
+ [self _unlockConnection];
+ return NO;
+ }
- keepAliveLastPingSuccess = NO;
+ volatile BOOL keepAliveLastPingSuccess = NO;
keepAliveLastPingBlocked = NO;
keepAlivePingThreadActive = YES;
@@ -148,12 +157,14 @@
SPMySQLConnectionPingDetails *pingDetails = malloc(sizeof(SPMySQLConnectionPingDetails));
pingDetails->mySQLConnection = mySQLConnection;
pingDetails->keepAliveLastPingSuccessPointer = &keepAliveLastPingSuccess;
- pingDetails->keepAlivePingActivePointer = &keepAlivePingThreadActive;
+ pingDetails->keepAlivePingThreadActivePointer = &keepAlivePingThreadActive;
// Create a pthread for the ping
+ pthread_t keepAlivePingThread_t;
+
pthread_attr_t attr;
pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&keepAlivePingThread_t, &attr, (void *)&_backgroundPingTask, pingDetails);
// Record the ping start time
@@ -166,7 +177,7 @@
// If the ping timeout has been exceeded, or the ping thread has been
// cancelled, force a timeout; double-check that the thread is still active.
- if (([keepAliveThread isCancelled] || pingElapsedTime > pingTimeout)
+ if (([[NSThread currentThread] isCancelled] || pingElapsedTime > pingTimeout)
&& keepAlivePingThreadActive
&& !threadCancelled)
{
@@ -182,6 +193,9 @@
keepAliveLastPingBlocked = YES;
}
} while (keepAlivePingThreadActive);
+
+ //wait for thread to go away, otherwise our free() below might run before _pingThreadCleanup()
+ pthread_join(keepAlivePingThread_t, NULL);
// Clean up
keepAlivePingThread_t = NULL;
@@ -238,7 +252,7 @@ void _forceThreadExit(int signalNumber)
void _pingThreadCleanup(void *pingDetails)
{
SPMySQLConnectionPingDetails *pingDetailsStruct = pingDetails;
- *(pingDetailsStruct->keepAlivePingActivePointer) = NO;
+ *(pingDetailsStruct->keepAlivePingThreadActivePointer) = NO;
// Clean up MySQL variables and handlers
mysql_thread_end();
@@ -250,24 +264,28 @@ void _pingThreadCleanup(void *pingDetails)
/**
* If a keepalive thread is active, cancel it, and wait a short time for it
* to exit.
+ *
+ * @return YES, if the thread exited within 10 seconds after canceling it
*/
-- (void)_cancelKeepAlives
+- (BOOL)_cancelKeepAlives
{
// If no keepalive thread is active, return
- if (!keepAliveThread) {
- return;
- }
+ if (keepAliveThread) {
- // Mark the thread as cancelled
- [keepAliveThread cancel];
+ // Mark the thread as cancelled
+ [keepAliveThread cancel];
- // Wait inside a time limit of ten seconds for it to exit
- uint64_t threadCancelStartTime_t = mach_absolute_time();
- do {
- usleep(100000);
- if (_elapsedSecondsSinceAbsoluteTime(threadCancelStartTime_t) > 10) break;
- } while (keepAliveThread);
+ // Wait inside a time limit of ten seconds for it to exit
+ uint64_t threadCancelStartTime_t = mach_absolute_time();
+ do {
+ usleep(100000);
+ if (_elapsedSecondsSinceAbsoluteTime(threadCancelStartTime_t) > 10) return NO;
+ } while (keepAliveThread);
+
+ }
+
+ return YES;
}
@end
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m
index 48f4fc1e..e5a48d48 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m
@@ -58,6 +58,9 @@
* Take a string and escapes any special character for safe use within a query; correctly
* escapes any characters within the string using the current connection encoding.
* Allows control over whether to also wrap the string in single quotes.
+ *
+ * WARNING: This method may return nil if the current thread is cancelled!
+ * You MUST check the isCancelled flag before using the result!
*/
- (NSString *)escapeString:(NSString *)theString includingQuotes:(BOOL)includeQuotes
{
@@ -72,11 +75,12 @@
}
return nil;
}
- if (![self _checkConnectionIfNecessary]) return nil;
// Ensure per-thread variables are set up
[self _validateThreadSetup];
+ if (![self _checkConnectionIfNecessary]) return nil;
+
// Perform a lossy conversion to bytes, using NSData to do the hard work. Preserves
// nul characters correctly.
NSData *cData = [theString dataUsingEncoding:stringEncoding allowLossyConversion:YES];
@@ -221,6 +225,9 @@
* the connection encoding.
* The result type desired can be specified, supporting either standard or streaming
* result sets.
+ *
+ * WARNING: This method may return nil if the current thread is cancelled!
+ * You MUST check the isCancelled flag before using the result!
*/
- (id)queryString:(NSString *)theQueryString usingEncoding:(NSStringEncoding)theEncoding withResultType:(SPMySQLResultType)theReturnType
{
@@ -288,6 +295,7 @@
// Lock the connection while it's actively in use
[self _lockConnection];
+ unsigned long long theAffectedRowCount;
while (queryAttemptsAllowed > 0) {
// While recording the overall execution time (including network lag!), run
@@ -296,6 +304,11 @@
queryStatus = mysql_real_query(mySQLConnection, queryBytes, queryBytesLength);
queryExecutionTime = _elapsedSecondsSinceAbsoluteTime(queryStartTime);
lastConnectionUsedTime = mach_absolute_time();
+
+ // "An integer greater than zero indicates the number of rows affected or retrieved.
+ // Zero indicates that no records were updated for an UPDATE statement, no rows matched the WHERE clause in the query or that no query has yet been executed.
+ // -1 indicates that the query returned an error or that, for a SELECT query, mysql_affected_rows() was called prior to calling mysql_store_result()."
+ theAffectedRowCount = mysql_affected_rows(mySQLConnection);
// If the query succeeded, no need to re-attempt.
if (!queryStatus) {
@@ -313,7 +326,7 @@
theSqlstate = [self _stringForCString:mysql_sqlstate(mySQLConnection)];
// Prevent retries if the query was cancelled or not a connection error
- if (lastQueryWasCancelled || ![SPMySQLConnection isErrorIDConnectionError:mysql_errno(mySQLConnection)]) {
+ if (lastQueryWasCancelled || ![SPMySQLConnection isErrorIDConnectionError:theErrorID]) {
break;
}
}
@@ -327,11 +340,11 @@
return nil;
}
[self _lockConnection];
+ NSAssert(mySQLConnection != NULL, @"mySQLConnection has disappeared while checking it!");
queryAttemptsAllowed--;
}
- unsigned long long theAffectedRowCount = mysql_affected_rows(mySQLConnection);
id theResult = nil;
// On success, if there is a query result, retrieve the result data type
@@ -660,6 +673,16 @@
}
/**
+ * Update lastErrorID, lastErrorMessage and lastSqlstate from connection
+ */
+- (void)_updateLastErrorInfos
+{
+ [self _updateLastErrorID:NSNotFound];
+ [self _updateLastErrorMessage:nil];
+ [self _updateLastSqlstate:nil];
+}
+
+/**
* Update the MySQL error message for this connection. If an error is supplied
* it will be stored and returned to anything asking the instance for the last
* error; if no error is supplied, the connection will be used to derive (or clear)
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.h
index 82607cdb..8ec6c9e0 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.h
@@ -45,4 +45,14 @@
- (SPMySQLResult *)listProcesses;
- (BOOL)killQueryOnThreadID:(unsigned long)theThreadID;
+/**
+ * mysql_shutdown() - If the user has the permission, will shutdown the (remote) server
+ * @return Whether the command was executed successfully
+ * Note: this can also be NO if the user denied a reconnect attempt.
+ *
+ * WARNING: This method may return NO if the current thread is cancelled!
+ * You MUST check the isCancelled flag before using the result!
+ */
+- (BOOL)serverShutdown;
+
@end
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m
index dd684c78..db846929 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m
@@ -46,54 +46,34 @@
return [NSString stringWithString:serverVariableVersion];
}
-#warning FIXME: There is probably a race condition here with -[self _disconnect]
- if(mySQLConnection) {
- return [self _stringForCString:mysql_get_server_info(mySQLConnection)];
- }
-
return nil;
}
/**
- * Return the server major version or NSNotFound on failure
+ * Return the server major version or 0 on failure
*/
- (NSUInteger)serverMajorVersion
{
- NSString *ver;
- if ((ver = [self serverVersionString]) != nil) {
- NSString *s = [[ver componentsSeparatedByString:@"."] objectAtIndex:0];
- return (NSUInteger)[s integerValue];
- }
-
- return NSNotFound;
+ // 5.5.33 => 50533 / 10'000 => 5.0533 => 5
+ return (serverVersionNumber / 10000);
}
/**
- * Return the server minor version or NSNotFound on failure
+ * Return the server minor version or 0 on failure
*/
- (NSUInteger)serverMinorVersion
{
- NSString *ver;
- if ((ver = [self serverVersionString]) != nil) {
- NSString *s = [[ver componentsSeparatedByString:@"."] objectAtIndex:1];
- return (NSUInteger)[s integerValue];
- }
-
- return NSNotFound;
+ // 5.5.33 => 50533 - (5*10'000) => 533 / 100 => 5.33 => 5
+ return ((serverVersionNumber - [self serverMajorVersion]*10000) / 100);
}
/**
- * Return the server release version or NSNotFound on failure
+ * Return the server release version or 0 on failure
*/
- (NSUInteger)serverReleaseVersion
{
- NSString *ver;
- if ((ver = [self serverVersionString]) != nil) {
- NSString *s = [[ver componentsSeparatedByString:@"."] objectAtIndex:2];
- return (NSUInteger)[[[s componentsSeparatedByString:@"-"] objectAtIndex:0] integerValue];
- }
-
- return NSNotFound;
+ // 5.5.33 => 50533 - (5*10'000 + 5*100) => 33
+ return (serverVersionNumber - ([self serverMajorVersion]*10000 + [self serverMinorVersion]*100));
}
#pragma mark -
@@ -105,23 +85,9 @@
*/
- (BOOL)serverVersionIsGreaterThanOrEqualTo:(NSUInteger)aMajorVersion minorVersion:(NSUInteger)aMinorVersion releaseVersion:(NSUInteger)aReleaseVersion
{
- NSString *ver;
- if (!(ver = [self serverVersionString])) return NO;
-
- NSArray *serverVersionParts = [ver componentsSeparatedByString:@"."];
-
- NSUInteger serverMajorVersion = (NSUInteger)[[serverVersionParts objectAtIndex:0] integerValue];
- if (serverMajorVersion < aMajorVersion) return NO;
- if (serverMajorVersion > aMajorVersion) return YES;
+ unsigned long myver = aMajorVersion * 10000 + aMinorVersion * 100 + aReleaseVersion;
- NSUInteger serverMinorVersion = (NSUInteger)[[serverVersionParts objectAtIndex:1] integerValue];
- if (serverMinorVersion < aMinorVersion) return NO;
- if (serverMinorVersion > aMinorVersion) return YES;
-
- NSString *serverReleasePart = [serverVersionParts objectAtIndex:2];
- NSUInteger serverReleaseVersion = (NSUInteger)[[[serverReleasePart componentsSeparatedByString:@"-"] objectAtIndex:0] integerValue];
- if (serverReleaseVersion < aReleaseVersion) return NO;
- return YES;
+ return (serverVersionNumber >= myver);
}
#pragma mark -
@@ -132,6 +98,9 @@
* the resulting process list defaults to the short form; run a manual SHOW FULL PROCESSLIST
* to retrieve tasks in non-truncated form.
* Returns nil on error.
+ *
+ * WARNING: This method may return nil if the current thread is cancelled!
+ * You MUST check the isCancelled flag before using the result!
*/
- (SPMySQLResult *)listProcesses
{
@@ -182,4 +151,21 @@
return ![self queryErrored];
}
+- (BOOL)serverShutdown
+{
+ if([self _checkConnectionIfNecessary]) {
+ [self _lockConnection];
+ // Ensure per-thread variables are set up
+ [self _validateThreadSetup];
+ //only SHUTDOWN_DEFAULT is supported right now
+ int res = mysql_shutdown(mySQLConnection, SHUTDOWN_DEFAULT);
+ //update or clear error
+ [self _updateLastErrorInfos];
+ [self _unlockConnection];
+
+ return (res == 0);
+ }
+ return NO;
+}
+
@end
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h
index c65ec2fb..15b809f1 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h
@@ -85,10 +85,8 @@
CGFloat keepAliveInterval;
uint64_t lastKeepAliveTime;
NSUInteger keepAlivePingFailures;
- NSThread *keepAliveThread;
- pthread_t keepAlivePingThread_t;
- BOOL keepAlivePingThreadActive;
- BOOL keepAliveLastPingSuccess;
+ volatile NSThread *keepAliveThread;
+ volatile BOOL keepAlivePingThreadActive;
BOOL keepAliveLastPingBlocked;
// Encoding details - and also a record of any previous encoding to allow
@@ -101,6 +99,7 @@
// Server details
NSString *serverVariableVersion;
+ unsigned long serverVersionNumber;
// Error state for the last query or connection state
NSUInteger queryErrorID;
@@ -129,6 +128,8 @@
BOOL retryQueriesOnConnectionFailure;
SPMySQLClientFlags clientFlags;
+
+ NSString *_debugLastConnectedEvent;
}
#pragma mark -
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
index 40c95321..b09a27ee 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
@@ -145,9 +145,7 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS
keepAlivePingFailures = 0;
lastKeepAliveTime = 0;
keepAliveThread = nil;
- keepAlivePingThread_t = NULL;
keepAlivePingThreadActive = NO;
- keepAliveLastPingSuccess = NO;
keepAliveLastPingBlocked = NO;
// Set up default encoding variables
@@ -176,6 +174,7 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS
// Ensure the server detail records are initialised
serverVariableVersion = nil;
+ serverVersionNumber = 0;
// Start with a blank error state
queryErrorID = 0;
@@ -200,6 +199,8 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS
// while running them
retryQueriesOnConnectionFailure = YES;
+ _debugLastConnectedEvent = nil;
+
// Start the ping keepalive timer
keepAliveTimer = [[SPMySQLKeepAliveTimer alloc] initWithInterval:10 target:self selector:@selector(_keepAlive)];
@@ -254,6 +255,8 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS
if (querySqlstate) [querySqlstate release], querySqlstate = nil;
[delegateDecisionLock release];
+ [_debugLastConnectedEvent release];
+
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[super dealloc];
@@ -279,6 +282,9 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS
* Error checks extensively - if this method fails, it will ask how to proceed and loop depending
* on the status, not returning control until either a connection has been established or
* the connection and document have been closed.
+ *
+ * WARNING: This method may exit early returning NO if the current thread is cancelled!
+ * You MUST check the isCancelled flag before using the result!
*/
- (BOOL)reconnect
{
@@ -327,10 +333,12 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS
* Checks whether the connection to the server is still active. This verifies
* the connection using a ping, and if the connection is found to be down attempts
* to quickly restore it, including the previous state.
+ *
+ * WARNING: This method may return NO if the current thread is cancelled!
+ * You MUST check the isCancelled flag before using the result!
*/
- (BOOL)checkConnection
{
-
// If the connection is not seen as active, don't proceed
if (state != SPMySQLConnected) return NO;
@@ -429,6 +437,14 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS
const char *__crashreporter_info__ = NULL;
asm(".desc ___crashreporter_info__, 0x10");
+static uint64_t _elapsedMicroSecondsSinceAbsoluteTime(uint64_t comparisonTime)
+{
+ uint64_t elapsedTime_t = mach_absolute_time() - comparisonTime;
+ Nanoseconds elapsedTime = AbsoluteToNanoseconds(*(AbsoluteTime *)&(elapsedTime_t));
+
+ return (UnsignedWideToUInt64(elapsedTime) / 1000ULL);
+}
+
@implementation SPMySQLConnection (PrivateAPI)
/**
@@ -439,8 +455,11 @@ asm(".desc ___crashreporter_info__, 0x10");
// If a connection is already active in some form, throw an exception
if (state != SPMySQLDisconnected && state != SPMySQLConnectionLostInBackground) {
- asprintf(&__crashreporter_info__, "Attempted to connect a connection that is not disconnected (SPMySQLConnectionState=%d).", state);
- __builtin_trap();
+ @synchronized (self) {
+ uint64_t diff = _elapsedMicroSecondsSinceAbsoluteTime(initialConnectTime);
+ asprintf(&__crashreporter_info__, "Attempted to connect a connection that is not disconnected (SPMySQLConnectionState=%d).\nIf state==2: Previous connection made %lluµs ago from: %s", state, diff, [_debugLastConnectedEvent cStringUsingEncoding:NSUTF8StringEncoding]);
+ __builtin_trap();
+ }
[NSException raise:NSInternalInconsistencyException format:@"Attempted to connect a connection that is not disconnected (SPMySQLConnectionState=%d).", state];
return NO;
}
@@ -466,17 +485,36 @@ asm(".desc ___crashreporter_info__, 0x10");
// If the connection was cancelled, clean up and don't continue
if (userTriggeredDisconnect) {
mysql_close(mySQLConnection);
- [self _unlockConnection];
mySQLConnection = NULL;
+ [self _unlockConnection];
return NO;
}
// Successfully connected - record connected state and reset tracking variables
state = SPMySQLConnected;
- initialConnectTime = mach_absolute_time();
+
+ @synchronized (self) {
+ initialConnectTime = mach_absolute_time();
+ [_debugLastConnectedEvent release];
+ _debugLastConnectedEvent = [[NSString alloc] initWithFormat:@"thread=%@ stack=%@",[NSThread currentThread],[NSThread callStackSymbols]];
+ }
+
mysqlConnectionThreadId = mySQLConnection->thread_id;
lastConnectionUsedTime = initialConnectTime;
+ // Copy the server version string to the instance variable
+ if (serverVariableVersion) [serverVariableVersion release], serverVariableVersion = nil;
+ // the mysql_get_server_info() function
+ // * returns the version name that is part of the initial connection handshake.
+ // * Unless the connection failed, it will always return a non-null buffer containing at least a '\0'.
+ // * It will never affect the error variables (since it only returns a struct member)
+ //
+ // At that point (handshake) there is no charset and it's highly unlikely this will ever contain something other than ASCII,
+ // but to be safe, we'll use the Latin1 encoding which won't bail on invalid chars.
+ serverVariableVersion = [[NSString alloc] initWithCString:mysql_get_server_info(mySQLConnection) encoding:NSISOLatin1StringEncoding];
+ // this one can actually change the error state, but only if the server version string is not set (ie. no connection)
+ serverVersionNumber = mysql_get_server_version(mySQLConnection);
+
// Update SSL state
connectedWithSSL = NO;
if (useSSL) connectedWithSSL = (mysql_get_ssl_cipher(mySQLConnection))?YES:NO;
@@ -491,9 +529,7 @@ asm(".desc ___crashreporter_info__, 0x10");
keepAlivePingFailures = 0;
// Clear the connection error record
- [self _updateLastErrorID:NSNotFound];
- [self _updateLastErrorMessage:nil];
- [self _updateLastSqlstate:nil];
+ [self _updateLastErrorInfos];
// Unlock the connection
[self _unlockConnection];
@@ -526,10 +562,7 @@ asm(".desc ___crashreporter_info__, 0x10");
// Calling mysql_init will have automatically installed per-thread variables if necessary,
// so track their installation for removal and to avoid recreating again.
- if (!pthread_getspecific(mySQLThreadInitFlagKey)) {
- pthread_setspecific(mySQLThreadInitFlagKey, &mySQLThreadFlag);
- [(NSNotificationCenter *)[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(_removeThreadVariables:) name:NSThreadWillExitNotification object:[NSThread currentThread]];
- }
+ [self _validateThreadSetup];
// Disable automatic reconnection, as it's handled in-framework to preserve
// options, encodings and connection state.
@@ -619,6 +652,9 @@ asm(".desc ___crashreporter_info__, 0x10");
* the connection and document have been closed.
* Runs its own autorelease pool as sometimes called in a thread following proxy changes
* (where the return code doesn't matter).
+ *
+ * WARNING: This method may exit early returning NO if the current thread is cancelled!
+ * You MUST check the isCancelled flag before using the result!
*/
- (BOOL)_reconnectAllowingRetries:(BOOL)canRetry
{
@@ -897,6 +933,7 @@ asm(".desc ___crashreporter_info__, 0x10");
[self _unlockConnection];
[self _cancelKeepAlives];
+ [self _lockConnection];
// Close the underlying MySQL connection if it still appears to be active, and not reading
// or writing. While this may result in a leak of the MySQL object, it prevents crashes
// due to attempts to close a blocked/stuck connection.
@@ -904,17 +941,16 @@ asm(".desc ___crashreporter_info__, 0x10");
mysql_close(mySQLConnection);
}
mySQLConnection = NULL;
+ if (serverVariableVersion) [serverVariableVersion release], serverVariableVersion = nil;
+ serverVersionNumber = 0;
+ if (database) [database release], database = nil;
+ state = SPMySQLDisconnected;
+ [self _unlockConnection];
// If using a connection proxy, disconnect that too
if (proxy) {
[proxy performSelectorOnMainThread:@selector(disconnect) withObject:nil waitUntilDone:YES];
}
-
- // Clear host-specific information
- if (serverVariableVersion) [serverVariableVersion release], serverVariableVersion = nil;
- if (database) [database release], database = nil;
-
- state = SPMySQLDisconnected;
}
/**
@@ -939,10 +975,6 @@ asm(".desc ___crashreporter_info__, 0x10");
[variables setObject:[variableRow objectAtIndex:1] forKey:[variableRow objectAtIndex:0]];
}
- // Copy the server version string to the instance variable
- if (serverVariableVersion) [serverVariableVersion release], serverVariableVersion = nil;
- serverVariableVersion = [[variables objectForKey:@"version"] retain];
-
// Get the connection encoding. Although a specific encoding may have been requested on
// connection, it may be overridden by init_connect commands or connection state changes.
// Default to latin1 for older server versions.
@@ -995,6 +1027,9 @@ asm(".desc ___crashreporter_info__, 0x10");
* each of which requires a round trip to the server - but handles most
* network issues.
* Returns whether the connection is considered still valid.
+ *
+ * WARNING: This method may return NO if the current thread is cancelled!
+ * You MUST check the isCancelled flag before using the result!
*/
- (BOOL)_checkConnectionIfNecessary
{
@@ -1018,21 +1053,22 @@ asm(".desc ___crashreporter_info__, 0x10");
* Ensure that the thread this method is called on has been registered for
* use with MySQL. MySQL requires thread-specific variables for safe
* execution.
+ *
+ * Calling this multiple times per thread is OK.
*/
- (void)_validateThreadSetup
{
-
// Check to see whether the handler has already been installed
if (pthread_getspecific(mySQLThreadInitFlagKey)) return;
// If not, install it
- mysql_thread_init();
+ mysql_thread_init(); // multiple calls per thread OK.
// Mark the thread to avoid multiple installs
pthread_setspecific(mySQLThreadInitFlagKey, &mySQLThreadFlag);
// Set up the notification handler to deregister it
- [(NSNotificationCenter *)[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(_removeThreadVariables:) name:NSThreadWillExitNotification object:[NSThread currentThread]];
+ [[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(_removeThreadVariables:) name:NSThreadWillExitNotification object:[NSThread currentThread]];
}
/**
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLFastStreamingResult.m b/Frameworks/SPMySQLFramework/Source/SPMySQLFastStreamingResult.m
index 930180da..53ab116f 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLFastStreamingResult.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLFastStreamingResult.m
@@ -392,9 +392,7 @@ typedef struct st_spmysqlstreamingrowdata {
}
// Update the connection's error statuses to reflect any errors during the content download
- [parentConnection _updateLastErrorID:NSNotFound];
- [parentConnection _updateLastErrorMessage:nil];
- [parentConnection _updateLastSqlstate:nil];
+ [parentConnection _updateLastErrorInfos];
// Unlock the parent connection now all data has been retrieved
[parentConnection _unlockConnection];
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.h b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.h
index 817d2cb7..7d865226 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.h
@@ -30,12 +30,7 @@
@interface SPMySQLResult (Data_Conversion_Private_API)
++ (void)_initializeDataConversion;
- (id)_getObjectFromBytes:(char *)bytes ofLength:(NSUInteger)length fieldDefinitionIndex:(NSUInteger)fieldIndex previewLength:(NSUInteger)previewLength;
-static inline SPMySQLResultFieldProcessor _processorForField(MYSQL_FIELD aField);
-
-static inline NSString * _stringWithBytes(const void *dataBytes, NSUInteger dataLength, NSStringEncoding aStringEncoding, NSUInteger previewLength);
-static inline NSString * _bitStringWithBytes(const char *bytes, NSUInteger length, NSUInteger padLength);
-static inline NSString * _convertStringData(const void *dataBytes, NSUInteger dataLength, NSStringEncoding aStringEncoding, NSUInteger previewLength);
-
@end
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m
index 639ff0b9..3b29fb5e 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m
@@ -31,6 +31,16 @@
#import "Data Conversion.h"
+#ifdef SPMYSQL_FOR_UNIT_TESTING
+#define PRIVATE /* public */
+#else
+#define PRIVATE static inline
+#endif
+
+PRIVATE SPMySQLResultFieldProcessor _processorForField(MYSQL_FIELD aField);
+PRIVATE NSString * _bitStringWithBytes(const char *bytes, NSUInteger length, NSUInteger padLength);
+PRIVATE NSString * _convertStringData(const void *dataBytes, NSUInteger dataLength, NSStringEncoding aStringEncoding, NSUInteger previewLength);
+
static SPMySQLResultFieldProcessor fieldProcessingMap[256];
static id NSNullPointer;
static NSStringEncoding NSFromCFStringEncodingBig5;
@@ -68,6 +78,7 @@ static NSStringEncoding NSFromCFStringEncodingGBK_95;
fieldProcessingMap[MYSQL_TYPE_NEWDATE] = SPMySQLResultFieldAsString;
fieldProcessingMap[MYSQL_TYPE_VARCHAR] = SPMySQLResultFieldAsString;
fieldProcessingMap[MYSQL_TYPE_BIT] = SPMySQLResultFieldAsBit;
+ fieldProcessingMap[MYSQL_TYPE_JSON] = SPMySQLResultFieldAsString;
fieldProcessingMap[MYSQL_TYPE_NEWDECIMAL] = SPMySQLResultFieldAsString;
fieldProcessingMap[MYSQL_TYPE_ENUM] = SPMySQLResultFieldAsString;
fieldProcessingMap[MYSQL_TYPE_SET] = SPMySQLResultFieldAsString;
@@ -161,10 +172,12 @@ static NSStringEncoding NSFromCFStringEncodingGBK_95;
return nil;
}
+@end
+
/**
* Returns the field processor to use for a specified field.
*/
-static inline SPMySQLResultFieldProcessor _processorForField(MYSQL_FIELD aField)
+PRIVATE SPMySQLResultFieldProcessor _processorForField(MYSQL_FIELD aField)
{
// Determine the default field processor to use
SPMySQLResultFieldProcessor dataProcessor = fieldProcessingMap[aField.type];
@@ -200,7 +213,7 @@ static inline SPMySQLResultFieldProcessor _processorForField(MYSQL_FIELD aField)
* field length.
* MySQL stores bit data as string data stored in an 8-bit wide character set.
*/
-static inline NSString * _bitStringWithBytes(const char *bytes, NSUInteger length, NSUInteger padLength)
+PRIVATE NSString * _bitStringWithBytes(const char *bytes, NSUInteger length, NSUInteger padLength)
{
NSUInteger i = 0;
NSUInteger bitLength = length << 3;
@@ -209,27 +222,26 @@ static inline NSString * _bitStringWithBytes(const char *bytes, NSUInteger lengt
return nil;
}
- // Ensure padLength is never lower than the length
- if (padLength < bitLength) {
- padLength = bitLength;
- }
-
+ // use whatever is smaller. padLength comes from BIT(x), bitLength from the actual bytes transmitted.
+ // if bitLength < padLength it means the value is smaller than what the field can accomodate.
+ // if bitLength > padLength it means BIT(x) is not a full n bytes long and was extended by mysqls storage.
+ // In that case the additional bits should still be 0 as mysql does not allow to set bits over the size of x.
+ bitLength = MIN(bitLength,padLength);
// Generate a nul-terminated C string representation of the binary data
char *cStringBuffer = malloc(padLength + 1);
- cStringBuffer[padLength] = '\0';
+ memset(cStringBuffer, '0', padLength);
while (i < bitLength)
{
+ // start with the least significant bit (the rightmost bit in the last byte) and move left
+ unsigned char bitInByteMask = i % 8; // 0-7, the cycle is 0,1,...,7,0,...
+ unsigned long bytesOffset = (length - 1) - (i >> 3); // i>>3 == floor(i/8)
++i;
-
- cStringBuffer[padLength - i] = ((bytes[length - 1 - (i >> 3)] >> (i & 0x7)) & 1 ) ? '1' : '0';
+ cStringBuffer[padLength - i] = ((bytes[bytesOffset] & (1 << bitInByteMask)) != 0) ? '1' : '0';
}
-
- while (i++ < padLength)
- {
- cStringBuffer[padLength - i] = '0';
- }
-
+
+ cStringBuffer[padLength] = '\0';
+
// Convert to a string
NSString *returnString = [NSString stringWithUTF8String:cStringBuffer];
@@ -243,7 +255,7 @@ static inline NSString * _bitStringWithBytes(const char *bytes, NSUInteger lengt
* Converts stored string data - which may contain nul bytes - to a native
* Objective-C string, using the current class encoding.
*/
-static inline NSString * _convertStringData(const void *dataBytes, NSUInteger dataLength, NSStringEncoding aStringEncoding, NSUInteger previewLength)
+PRIVATE NSString * _convertStringData(const void *dataBytes, NSUInteger dataLength, NSStringEncoding aStringEncoding, NSUInteger previewLength)
{
// Fast case - if not using a preview length, or if the data length is shorter, return the requested data.
@@ -414,5 +426,4 @@ static inline NSString * _convertStringData(const void *dataBytes, NSUInteger da
return previewString;
}
-
-@end
+#undef PRIVATE
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m
index c61b9140..ec52e0e3 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m
@@ -29,6 +29,7 @@
// More info at <https://github.com/sequelpro/sequelpro>
#import "Field Definitions.h"
+#import "SPMySQL Private APIs.h"
@interface SPMySQLResult (Field_Definitions_Private_API)
@@ -40,14 +41,6 @@
@end
-// Import a private declaration from the SPMySQLResult file for use
-@interface SPMySQLResult (Private_API)
-
-- (NSString *)_stringWithBytes:(const void *)bytes length:(NSUInteger)length;
-- (NSString *)_lossyStringWithBytes:(const void *)bytes length:(NSUInteger)length wasLossy:(BOOL *)outLossy;
-
-@end
-
#define MAGIC_BINARY_CHARSET_NR 63
const SPMySQLResultCharset SPMySQLCharsetMap[] =
@@ -379,7 +372,7 @@ const SPMySQLResultCharset SPMySQLCharsetMap[] =
switch (type) {
- case FIELD_TYPE_BIT:
+ case MYSQL_TYPE_BIT:
return @"BIT";
case MYSQL_TYPE_DECIMAL:
@@ -482,6 +475,9 @@ const SPMySQLResultCharset SPMySQLCharsetMap[] =
case MYSQL_TYPE_GEOMETRY:
return @"GEOMETRY";
+
+ case MYSQL_TYPE_JSON:
+ return @"JSON";
default:
return @"UNKNOWN";
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m b/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m
index 5f54960c..2e1cb2ba 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m
@@ -318,7 +318,7 @@ static id NSNullPointer;
{
return [[[NSString alloc] initWithBytes:bytes length:length encoding:stringEncoding] autorelease];
}
-
+#warning duplicate code with Data Conversion.m stringForDataBytes:length:encoding: (↑, ↓)
- (NSString *)_lossyStringWithBytes:(const void *)bytes length:(NSUInteger)length wasLossy:(BOOL *)outLossy
{
if(!bytes || !length) return @""; //to match -[NSString initWithBytes:length:encoding:]
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLStreamingResultStore.m b/Frameworks/SPMySQLFramework/Source/SPMySQLStreamingResultStore.m
index 86a6b2b5..29f83e0e 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLStreamingResultStore.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLStreamingResultStore.m
@@ -809,9 +809,7 @@ static inline void SPMySQLStreamingResultStoreFreeRowData(SPMySQLStreamingResult
}
// Update the connection's error statuses to reflect any errors during the content download
- [parentConnection _updateLastErrorID:NSNotFound];
- [parentConnection _updateLastErrorMessage:nil];
- [parentConnection _updateLastSqlstate:nil];
+ [parentConnection _updateLastErrorInfos];
// Unlock the parent connection now all data has been retrieved
[parentConnection _unlockConnection];