diff options
-rw-r--r-- | Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql_com.h | 6 | ||||
-rw-r--r-- | Frameworks/SPMySQLFramework/SPMySQLDataTypes.h | 1 | ||||
-rw-r--r-- | Frameworks/SPMySQLFramework/SPMySQLDataTypes.m | 1 | ||||
-rw-r--r-- | Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m | 1 | ||||
-rw-r--r-- | Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m | 5 | ||||
-rw-r--r-- | Resources/Plists/CompletionTokens.plist | 64 | ||||
-rw-r--r-- | Source/SPEditorTokens.l | 3 | ||||
-rw-r--r-- | Source/SPTableData.m | 3 | ||||
-rw-r--r-- | Source/SPTableStructure.m | 27 | ||||
-rw-r--r-- | Source/SPTableStructureDelegate.m | 18 | ||||
-rw-r--r-- | Source/SPTableStructureLoading.m | 10 |
11 files changed, 119 insertions, 20 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/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/Source/SPMySQLResult Categories/Data Conversion.m b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m index b2336aaa..3b29fb5e 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m @@ -78,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; diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m index 09bd9580..ec52e0e3 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m @@ -372,7 +372,7 @@ const SPMySQLResultCharset SPMySQLCharsetMap[] = switch (type) { - case FIELD_TYPE_BIT: + case MYSQL_TYPE_BIT: return @"BIT"; case MYSQL_TYPE_DECIMAL: @@ -475,6 +475,9 @@ const SPMySQLResultCharset SPMySQLCharsetMap[] = case MYSQL_TYPE_GEOMETRY: return @"GEOMETRY"; + + case MYSQL_TYPE_JSON: + return @"JSON"; default: return @"UNKNOWN"; diff --git a/Resources/Plists/CompletionTokens.plist b/Resources/Plists/CompletionTokens.plist index f089401d..fc10db70 100644 --- a/Resources/Plists/CompletionTokens.plist +++ b/Resources/Plists/CompletionTokens.plist @@ -289,6 +289,7 @@ <string>ISSUER</string> <string>ITERATE</string> <string>JOIN</string> + <string>JSON</string> <string>KEY</string> <string>KEYS</string> <string>KEY_BLOCK_SIZE</string> @@ -834,6 +835,27 @@ <string>ISSIMPLE</string> <string>IS_FREE_LOCK</string> <string>IS_USED_LOCK</string> + <string>JSON_APPEND</string> + <string>JSON_ARRAY_APPEND</string> + <string>JSON_ARRAY_INSERT</string> + <string>JSON_ARRAY</string> + <string>JSON_CONTAINS_PATH</string> + <string>JSON_CONTAINS</string> + <string>JSON_DEPTH</string> + <string>JSON_EXTRACT</string> + <string>JSON_INSERT</string> + <string>JSON_KEYS</string> + <string>JSON_LENGTH</string> + <string>JSON_MERGE</string> + <string>JSON_OBJECT</string> + <string>JSON_QUOTE</string> + <string>JSON_REMOVE</string> + <string>JSON_REPLACE</string> + <string>JSON_SEARCH</string> + <string>JSON_SET</string> + <string>JSON_TYPE</string> + <string>JSON_UNQUOTE</string> + <string>JSON_VALID</string> <string>LAST_DAY</string> <string>LAST_INSERT_ID</string> <string>LCASE</string> @@ -1162,6 +1184,48 @@ <string>${1:bits}, ${2:str1}</string> <key>FIND_IN_SET</key> <string>${1:str}, ${2:strlist}</string> + <key>JSON_APPEND</key> + <string>${1:json_doc}, ${2:path}, ${3:val}${4:, ${5:path}, ${6:val}${7:, ${8:...}}}</string> + <key>JSON_ARRAY_APPEND</key> + <string>${1:json_doc}, ${2:path}, ${3:val}${4:, ${5:path}, ${6:val}${7:, ${8:...}}}</string> + <key>JSON_ARRAY_INSERT</key> + <string>${1:json_doc}, ${2:path}, ${3:val}${4:, ${5:path}, ${6:val}${7:, ${8:...}}}</string> + <key>JSON_ARRAY</key> + <string>${1:${2:val} ${3:, ${4:val}${5:, ${6:...}}}}</string> + <key>JSON_CONTAINS_PATH</key> + <string>${1:json_doc}, ${2:¦'one'¦'all'¦}, ${3:path}${4:, ${5:path}${6:, ${7:...}}}</string> + <key>JSON_CONTAINS</key> + <string>${1:json_doc}, ${2:val}${3:, ${4:path}}</string> + <key>JSON_DEPTH</key> + <string>${1:json_doc}</string> + <key>JSON_EXTRACT</key> + <string>${1:json_doc}, ${2:path}${3:, ${4:path}${5:, ${6:...}}}</string> + <key>JSON_INSERT</key> + <string>${1:json_doc}, ${2:path}, ${3:val}${4:, ${5:path}, ${6:val}${7:, ${8:...}}}</string> + <key>JSON_KEYS</key> + <string>${1:json_doc}${2:, ${3:path}}</string> + <key>JSON_LENGTH</key> + <string>${1:json_doc}${2:, ${3:path}}</string> + <key>JSON_MERGE</key> + <string>${1:json_doc}, ${2:json_doc}${3:, ${4:...}}</string> + <key>JSON_OBJECT</key> + <string>${1:${2:key}, ${3:val}${4:, ${5:key}, ${6:val}${7:, ${8:...}}}}</string> + <key>JSON_QUOTE</key> + <string>${1:json_val}</string> + <key>JSON_REMOVE</key> + <string>${1:json_doc}, ${2:path}${3:, ${4:path}${5:, ${6:...}}}</string> + <key>JSON_REPLACE</key> + <string>${1:json_doc}, ${2:path}, ${3:val}${4:, ${5:path}, ${6:val}${7:, ${8:...}}}</string> + <key>JSON_SEARCH</key> + <string>${1:json_doc}, ${2:¦'one'¦'all'¦}, ${3:search_str}${4:, ${5:escape_char}${6:, ${7:path${8:, ${9:...}}}}}</string> + <key>JSON_SET</key> + <string>${1:json_doc}, ${2:path}, ${3:val}${4:, ${5:path}, ${6:val}${7:, ${8:...}}}</string> + <key>JSON_TYPE</key> + <string>${1:json_val}</string> + <key>JSON_UNQUOTE</key> + <string>${1:val}</string> + <key>JSON_VALID</key> + <string>${1:val}</string> </dict> </dict> </plist> diff --git a/Source/SPEditorTokens.l b/Source/SPEditorTokens.l index 2ac99be8..d3fac252 100644 --- a/Source/SPEditorTokens.l +++ b/Source/SPEditorTokens.l @@ -61,7 +61,7 @@ numeric ([+-]?(([0-9]+\.[0-9]+)|([0-9]*\.[0-9]+)|([0-9]+))(e[+-]?[0-9]+)?) ops "+"|"-"|"*"|"/" word [a-z_\.0-9\x80-\xEF@] variable @{1,2}[a-z_\.0-9\x80-\xEF$]+ -keyworda (G(R(OUP{s}BY|ANT(S)?)|E(NERAL|T_FORMAT|OMETRY(COLLECTION)?)|LOBAL)|B(Y(TE)?|TREE|I(GINT|N(LOG|ARY)|T)|O(TH|OL(EAN)?)|E(GIN|TWEEN|FORE)|LOB|ACKUP{s}TABLE)|H(IGH_PRIORITY|O(ST(S)?|UR(_(MI(NUTE|CROSECOND)|SECOND))?)|ELP|A(SH|NDLER|VING))|C(R(OSS|EATE)|H(ECK(SUM)?|A(R(SET|ACTER)?|NGE(D)?|IN))|IPHER|O(M(M(IT(TED)?|ENT)|P(RESSED|LETION|ACT))|N(S(TRAINT(_(SCHEMA|NAME|CATALOG))?|ISTENT)|NECTION|CURRENT|T(RIBUTORS|INUE|AINS)|DITION|VERT)|DE|L(UMN(S|_(NAME|FORMAT))?|LATE)|ALESCE{s}PARTITION)|U(R(RENT_(TIME(STAMP)?|DATE|USER)|SOR(_NAME)?)|BE)|L(IENT|OSE|ASS_ORIGIN)|A(S(CADE(D)?|E)|CHE{s}INDEX|TALOG_NAME|LL))|I(GNORE(_SERVER_IDS)?|MPORT{s}TABLESPACE|S(SUER|OLATION)?|N(S(TALL({s}PLUGIN)?|E(RT(_METHOD)?|NSITIVE))|N(O(BASE|DB)|ER)|T(1|2|8|3|O({s}(DUMP|OUT)FILE)?|4|E(RVAL|GER))?|ITIAL_SIZE|OUT|DEX(ES)?|VOKER|FILE)?|TERATE|O_THREAD|DENTIFIED|F)|D(ROP|YNAMIC|I(RECTORY|S(CARD{s}TABLESPACE|TINCT(ROW)?|K|ABLE{s}KEYS)|V)|O(UBLE)?|U(MPFILE|PLICATE|AL)|E(S(C(RIBE)?|_KEY_FILE)|C(IMAL|LARE)?|TERMINISTIC|F(INER|AULT)|L(ETE|AY(_KEY_WRITE|ED))|ALLOCATE)|A(Y(_(MI(NUTE|CROSECOND)|SECOND|HOUR))?|T(E(TIME)?|A(BASE(S)?|FILE)?)))|JOIN|E(RRORS|X(TEN(T_SIZE|DED)|I(STS|T)|P(LAIN|ANSION)|ECUTE)|SCAPE(D{s}BY)?|N(GINE(S)?|CLOSED{s}BY|D(S)?|UM|ABLE{s}KEYS)|VE(RY|NT)|LSE(IF)?|ACH)|K(ILL({s}(CONNECTION|QUERY))?|EY(S|_BLOCK_SIZE)?)|F(R(OM|AC_SECOND)|I(RST|XED|LE)|O(R(CE|EIGN)?|UND)|U(NCTION|LL(TEXT)?)|ETCH|L(OAT(8|4)?|USH)|A(ST|LSE))|A(G(GREGATE|AINST)|S(C(II)?|ENSITIVE)?|N(Y|D|ALYZE)|C(CESSIBLE|TION)|T|DD|UT(HORS|O(_INCREMENT|EXTEND_SIZE))|VG(_ROW_LENGTH)?|FTER|L(GORITHM|TER|L))) +keyworda (G(R(OUP{s}BY|ANT(S)?)|E(NERAL|T_FORMAT|OMETRY(COLLECTION)?)|LOBAL)|B(Y(TE)?|TREE|I(GINT|N(LOG|ARY)|T)|O(TH|OL(EAN)?)|E(GIN|TWEEN|FORE)|LOB|ACKUP{s}TABLE)|H(IGH_PRIORITY|O(ST(S)?|UR(_(MI(NUTE|CROSECOND)|SECOND))?)|ELP|A(SH|NDLER|VING))|C(R(OSS|EATE)|H(ECK(SUM)?|A(R(SET|ACTER)?|NGE(D)?|IN))|IPHER|O(M(M(IT(TED)?|ENT)|P(RESSED|LETION|ACT))|N(S(TRAINT(_(SCHEMA|NAME|CATALOG))?|ISTENT)|NECTION|CURRENT|T(RIBUTORS|INUE|AINS)|DITION|VERT)|DE|L(UMN(S|_(NAME|FORMAT))?|LATE)|ALESCE{s}PARTITION)|U(R(RENT_(TIME(STAMP)?|DATE|USER)|SOR(_NAME)?)|BE)|L(IENT|OSE|ASS_ORIGIN)|A(S(CADE(D)?|E)|CHE{s}INDEX|TALOG_NAME|LL))|I(GNORE(_SERVER_IDS)?|MPORT{s}TABLESPACE|S(SUER|OLATION)?|N(S(TALL({s}PLUGIN)?|E(RT(_METHOD)?|NSITIVE))|N(O(BASE|DB)|ER)|T(1|2|8|3|O({s}(DUMP|OUT)FILE)?|4|E(RVAL|GER))?|ITIAL_SIZE|OUT|DEX(ES)?|VOKER|FILE)?|TERATE|O_THREAD|DENTIFIED|F)|D(ROP|YNAMIC|I(RECTORY|S(CARD{s}TABLESPACE|TINCT(ROW)?|K|ABLE{s}KEYS)|V)|O(UBLE)?|U(MPFILE|PLICATE|AL)|E(S(C(RIBE)?|_KEY_FILE)|C(IMAL|LARE)?|TERMINISTIC|F(INER|AULT)|L(ETE|AY(_KEY_WRITE|ED))|ALLOCATE)|A(Y(_(MI(NUTE|CROSECOND)|SECOND|HOUR))?|T(E(TIME)?|A(BASE(S)?|FILE)?)))|J(OI|SO)N|E(RRORS|X(TEN(T_SIZE|DED)|I(STS|T)|P(LAIN|ANSION)|ECUTE)|SCAPE(D{s}BY)?|N(GINE(S)?|CLOSED{s}BY|D(S)?|UM|ABLE{s}KEYS)|VE(RY|NT)|LSE(IF)?|ACH)|K(ILL({s}(CONNECTION|QUERY))?|EY(S|_BLOCK_SIZE)?)|F(R(OM|AC_SECOND)|I(RST|XED|LE)|O(R(CE|EIGN)?|UND)|U(NCTION|LL(TEXT)?)|ETCH|L(OAT(8|4)?|USH)|A(ST|LSE))|A(G(GREGATE|AINST)|S(C(II)?|ENSITIVE)?|N(Y|D|ALYZE)|C(CESSIBLE|TION)|T|DD|UT(HORS|O(_INCREMENT|EXTEND_SIZE))|VG(_ROW_LENGTH)?|FTER|L(GORITHM|TER|L))) keywordl (R(TREE|IGHT|O(UTINE|W(S|_FORMAT)?|LL(BACK|UP))|E(GEXP|MOVE{s}PARTITIONING|BUILD{s}PARTITION|S(T(RICT|ORE{s}TABLE)|IGNAL|UME|ET)|NAME|COVER|TURN(S)?|ORGANIZE{s}PARTITION|D(O(_BUFFER_SIZE|FILE)|UNDANT)|P(EAT(ABLE)?|L(ICATION|ACE)|AIR)|VOKE|QUIRE|FERENCES|L(OAD|EASE|AY_(THREAD|LOG_(POS|FILE)))|A(D(S|_(ONLY|WRITE))?|L))|LIKE|ANGE)|M(YSQL_ERRNO|I(GRATE|N(_ROWS|UTE(_(MICROSECOND|SECOND))?)|CROSECOND|DDLEINT)|O(NTH|D(IF(Y|IES)|E)?)|U(TEX|LTI(PO(INT|LYGON)|LINESTRING))|E(RGE|MORY|SSAGE_TEXT|DIUM(BLOB|TEXT|INT)?)|A(X(_(ROWS|SIZE|CONNECTIONS_PER_HOUR|U(SER_CONNECTIONS|PDATES_PER_HOUR)|QUERIES_PER_HOUR)|VALUE)|STER(_(S(SL(_(C(IPHER|ERT|A(PATH)?)|VERIFY_SERVER_CERT|KEY))?|ERVER_ID)|H(OST|EARTBEAT_PERIOD)|CONNECT_RETRY|USER|P(ORT|ASSWORD)|LOG_(POS|FILE)))?|TCH))|N(CHAR|O(NE|_W(RITE_TO_BINLOG|AIT)|T|DEGROUP)?|DB(CLUSTER)?|U(MERIC|LL)|E(XT|W)|VARCHAR|A(ME(S)?|T(IONAL|URAL)))|O(R(DER{s}BY)?|N(({s}DUPLICATE{s}KEY{s}UPDATE)?|E(_SHOT)?|LINE)?|UT(ER|FILE)?|P(TI(MIZE|ON(S|ALLY)?)|EN)|FF(SET|LINE)|WNER|LD_PASSWORD)|P(R(I(MARY|VILEGES)|OCE(SS|DURE{s}(ANALYSE)?)|E(SERVE|CISION|PARE|V))|HASE|O(RT|INT|LYGON)|URGE|A(R(SER|TI(TION(S|ING)?|AL))|SSWORD|CK_KEYS))|QU(ICK|ERY|ARTER)|L(I(MIT|ST|NE(S(TRING)?|AR)|KE)|O(G(S|FILE({s}GROUP))|NG(BLOB|TEXT)?|C(K(S)?|AL(TIME(STAMP)?)?)|OP|W_PRIORITY|AD{s}(DATA|INDEX{s}INTO{s}CACHE|XML))|E(SS|VEL|FT|A(DING|VE(S)?))|A(ST|NGUAGE))) keywords (X(OR|509|A)|S(MALLINT|SL|H(OW({s}(E(NGINE(S)?|RRORS)|M(ASTER|UTEX)|BINLOG|GRANTS|INNODB|P(RIVILEGES|ROFILE(S)?|ROCEDURE{s}CODE)|SLAVE{s}(HOSTS|STATUS)|TRIGGERS|VARIABLES|WARNINGS|(FULL{s})?PROCESSLIST|FIELDS|PLUGIN(S)?|STORAGE{s}ENGINES|TABLE{s}TYPES|CO(LUMNS|LLATION)|BINLOG{s}EVENTS))?|UTDOWN|ARE)|NAPSHOT|CHE(MA(S|_NAME)?|DULE(R)?)|T(R(ING|AIGHT_JOIN)|O(RAGE|P)|A(RT(S|ING{s}BY)?|TUS))|I(GN(ED|AL)|MPLE)|O(ME|NAME|CKET|UNDS)|U(B(CLASS_ORIGIN|JECT|PARTITION(S)?)|SPEND|PER)|P(ECIFIC|ATIAL)|E(R(IAL(IZABLE)?|VER)|SSION|NSITIVE|C(OND(_MICROSECOND)?|URITY)|T({s}(PASSWORD|NAMES|ONE_SHOT))?|PARATOR|LECT)|QL(STATE|_(MAX_JOIN_SIZE|B(IG_(RESULT|SELECTS|TABLES)|UFFER_RESULT)|S(MALL_RESULT|ELECT_LIMIT|LAVE_SKIP_COUNTER|AFE_UPDATES)|NO_CACHE|CA(CHE|LC_FOUND_ROWS)|T(SI_(M(INUTE|ONTH)|SECOND|HOUR|YEAR|DAY|QUARTER|FRAC_SECOND|WEEK)|HREAD)|QUOTE_SHOW_CREATE|WARNINGS|LO(G_(BIN|OFF|UPDATE)|W_PRIORITY_UPDATES)|AUTO_IS_NULL)|EXCEPTION|WARNING)?|L(OW|AVE)|AVEPOINT)|YEAR(_MONTH)?|T(R(IGGER(S)?|U(NCATE|E)|A(NSACTION|ILING))|H(EN|AN)|YPE|I(ME(STAMP(DIFF|ADD)?)?|NY(BLOB|TEXT|INT))|O|E(RMINATED{s}BY|XT|MP(TABLE|ORARY))|ABLE(S(PACE)?|_NAME)?)|ZEROFILL|U(S(ING|E(R(_RESOURCES)?|_FRM)?|AGE)|N(SIGNED|COMMITTED|TIL|I(NSTALL({s}PLUGIN)?|CODE|ON|QUE)|D(O(_BUFFER_SIZE|FILE)?|EFINED)|KNOWN|LOCK)|TC_(TIME(STAMP)?|DATE)|P(GRADE|DATE))|V(IEW|A(R(BINARY|YING|CHAR(ACTER)?|IABLES)|LUE(S)?))|W(R(ITE|APPER)|H(ILE|E(RE|N))|ITH({s}PARSER)?|ORK|EEK|A(RNINGS|IT))) @@ -348,6 +348,7 @@ ISOLATION ISSUER ITERATE JOIN +JSON KEY KEYS KEY_BLOCK_SIZE diff --git a/Source/SPTableData.m b/Source/SPTableData.m index 62732b51..689ecc8f 100644 --- a/Source/SPTableData.m +++ b/Source/SPTableData.m @@ -1267,7 +1267,8 @@ } else if ([detailString isEqualToString:@"ENUM"] || [detailString isEqualToString:@"SET"]) { [fieldDetails setObject:@"enum" forKey:@"typegrouping"]; } else if ([detailString isEqualToString:@"TINYTEXT"] || [detailString isEqualToString:@"TEXT"] - || [detailString isEqualToString:@"MEDIUMTEXT"] || [detailString isEqualToString:@"LONGTEXT"]) { + || [detailString isEqualToString:@"MEDIUMTEXT"] || [detailString isEqualToString:@"LONGTEXT"] + || [detailString isEqualToString:@"JSON"]) { // JSON is seen as a text type by us, but works a bit different (e.g. encoding is always "utf8mb4") [fieldDetails setObject:@"textdata" forKey:@"typegrouping"]; } else if ([detailString isEqualToString:@"POINT"] || [detailString isEqualToString:@"GEOMETRY"] || [detailString isEqualToString:@"LINESTRING"] || [detailString isEqualToString:@"POLYGON"] diff --git a/Source/SPTableStructure.m b/Source/SPTableStructure.m index 81d0c531..2347f12b 100644 --- a/Source/SPTableStructure.m +++ b/Source/SPTableStructure.m @@ -194,6 +194,7 @@ static inline SPFieldTypeHelp *MakeFieldTypeHelp(NSString *typeName,NSString *ty SPMySQLLongBlobType, SPMySQLBinaryType, SPMySQLVarBinaryType, + SPMySQLJsonType, SPMySQLEnumType, SPMySQLSetType, @"--------", @@ -959,7 +960,10 @@ static inline SPFieldTypeHelp *MakeFieldTypeHelp(NSString *typeName,NSString *ty if(!specialFieldTypes) { - if ([fieldValidation isFieldTypeString:theRowType]) { + if ([theRowType isEqualToString:@"JSON"]) { + // we "see" JSON as a string, but it is not internally to MySQL and so doesn't allow CHARACTER SET/BINARY/COLLATE either. + } + else if ([fieldValidation isFieldTypeString:theRowType]) { BOOL charsetSupport = [[tableDocumentInstance serverSupport] supportsPost41CharacterSetHandling]; // Add CHARSET @@ -1002,8 +1006,9 @@ static inline SPFieldTypeHelp *MakeFieldTypeHelp(NSString *typeName,NSString *ty // Don't provide any defaults for auto-increment fields if (![theRowExtra isEqualToString:@"AUTO_INCREMENT"]) { NSArray *matches; + NSString *defaultValue = [theRow objectForKey:@"default"]; // If a NULL value has been specified, and NULL is allowed, specify DEFAULT NULL - if ([[theRow objectForKey:@"default"] isEqualToString:[prefs objectForKey:SPNullValue]]) + if ([defaultValue isEqualToString:[prefs objectForKey:SPNullValue]]) { if ([[theRow objectForKey:@"null"] integerValue] == 1) { [queryString appendString:@"\n DEFAULT NULL"]; @@ -1011,7 +1016,7 @@ static inline SPFieldTypeHelp *MakeFieldTypeHelp(NSString *typeName,NSString *ty } // Otherwise, if CURRENT_TIMESTAMP was specified for timestamps/datetimes, use that else if ([theRowType isInArray:@[@"TIMESTAMP",@"DATETIME"]] && - [(matches = [[[theRow objectForKey:@"default"] uppercaseString] captureComponentsMatchedByRegex:SPCurrentTimestampPattern]) count]) + [(matches = [[defaultValue uppercaseString] captureComponentsMatchedByRegex:SPCurrentTimestampPattern]) count]) { [queryString appendString:@"\n DEFAULT CURRENT_TIMESTAMP"]; NSString *userLen = [matches objectAtIndex:1]; @@ -1023,17 +1028,17 @@ static inline SPFieldTypeHelp *MakeFieldTypeHelp(NSString *typeName,NSString *ty } // If the field is of type BIT, permit the use of single qoutes and also don't quote the default value. // For example, use DEFAULT b'1' as opposed to DEFAULT 'b\'1\'' which results in an error. - else if ([(NSString *)[theRow objectForKey:@"default"] length] && [theRowType isEqualToString:@"BIT"]) { - [queryString appendFormat:@"\n DEFAULT %@", [theRow objectForKey:@"default"]]; + else if ([defaultValue length] && [theRowType isEqualToString:@"BIT"]) { + [queryString appendFormat:@"\n DEFAULT %@", defaultValue]; } // Suppress appending DEFAULT clause for any numerics, date, time fields if default is empty to avoid error messages; - // also don't specify a default for TEXT/BLOB or geometry fields to avoid strict mode errors - else if (![(NSString *)[theRow objectForKey:@"default"] length] && ([fieldValidation isFieldTypeNumeric:theRowType] || [fieldValidation isFieldTypeDate:theRowType] || [theRowType hasSuffix:@"TEXT"] || [theRowType hasSuffix:@"BLOB"] || [fieldValidation isFieldTypeGeometry:theRowType])) { + // also don't specify a default for TEXT/BLOB, JSON or geometry fields to avoid strict mode errors + else if (![defaultValue length] && ([fieldValidation isFieldTypeNumeric:theRowType] || [fieldValidation isFieldTypeDate:theRowType] || [theRowType hasSuffix:@"TEXT"] || [theRowType hasSuffix:@"BLOB"] || [theRowType isEqualToString:@"JSON"] || [fieldValidation isFieldTypeGeometry:theRowType])) { ; } // Otherwise, use the provided default else { - [queryString appendFormat:@"\n DEFAULT %@", [mySQLConnection escapeAndQuoteString:[theRow objectForKey:@"default"]]]; + [queryString appendFormat:@"\n DEFAULT %@", [mySQLConnection escapeAndQuoteString:defaultValue]]; } } @@ -1718,6 +1723,12 @@ static inline SPFieldTypeHelp *MakeFieldTypeHelp(NSString *typeName,NSString *ty NSLocalizedString(@"A byte array with variable length. The actual number of bytes is further limited by the values of other fields in the row.",@"description of varbinary") ), MakeFieldTypeHelp( + SPMySQLJsonType, + @"JSON", + NSLocalizedString(@"Limited to @@max_allowed_packet", @"range for json type"), + NSLocalizedString(@"A data type that validates JSON data on INSERT and internally stores it in a binary format that is both, more compact and faster to access than textual JSON.\nAvailable from MySQL 5.7.8.", @"description of json") + ), + MakeFieldTypeHelp( SPMySQLEnumType, @"ENUM('member',...)", [NSString stringWithFormat:NSLocalizedString(@"Up to %@ distinct members (<%@ in practice)\n1-2 bytes storage", @"range for enum type"),FN(@(65535)),FN(@3000)], diff --git a/Source/SPTableStructureDelegate.m b/Source/SPTableStructureDelegate.m index a7fa9337..755cc2b8 100644 --- a/Source/SPTableStructureDelegate.m +++ b/Source/SPTableStructureDelegate.m @@ -540,13 +540,14 @@ static void _BuildMenuWithPills(NSMenu *menu,struct _cmpMap *map,size_t mapEntri rowType = [[rowType stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString]; } - // Only string fields allow encoding settings + // Only string fields allow encoding settings, but JSON only uses utf8mb4 if (([[tableColumn identifier] isEqualToString:@"encoding"])) { - [aCell setEnabled:([fieldValidation isFieldTypeString:rowType] && [[tableDocumentInstance serverSupport] supportsPost41CharacterSetHandling])]; + [aCell setEnabled:(![rowType isEqualToString:@"JSON"] && [fieldValidation isFieldTypeString:rowType] && [[tableDocumentInstance serverSupport] supportsPost41CharacterSetHandling])]; } // Only string fields allow collation settings and string field is not set to BINARY since BINARY sets the collation to *_bin else if ([[tableColumn identifier] isEqualToString:@"collation"]) { + // JSON always uses utf8mb4_bin which is already covered by this logic [aCell setEnabled:([fieldValidation isFieldTypeString:rowType] && [[row objectForKey:@"binary"] integerValue] == 0 && [[tableDocumentInstance serverSupport] supportsPost41CharacterSetHandling])]; } @@ -557,12 +558,13 @@ static void _BuildMenuWithPills(NSMenu *menu,struct _cmpMap *map,size_t mapEntri // Check if BINARY is allowed else if ([[tableColumn identifier] isEqualToString:@"binary"]) { - [aCell setEnabled:([fieldValidation isFieldTypeAllowBinary:rowType])]; + // JSON always uses utf8mb4_bin + [aCell setEnabled:(![rowType isEqualToString:@"JSON"] && [fieldValidation isFieldTypeAllowBinary:rowType])]; } - // TEXT, BLOB, and GEOMETRY fields don't allow a DEFAULT + // TEXT, BLOB, GEOMETRY and JSON fields don't allow a DEFAULT else if ([[tableColumn identifier] isEqualToString:@"default"]) { - [aCell setEnabled:([rowType hasSuffix:@"TEXT"] || [rowType hasSuffix:@"BLOB"] || [fieldValidation isFieldTypeGeometry:rowType]) ? NO : YES]; + [aCell setEnabled:([rowType hasSuffix:@"TEXT"] || [rowType hasSuffix:@"BLOB"] || [rowType isEqualToString:@"JSON"] || [fieldValidation isFieldTypeGeometry:rowType]) ? NO : YES]; } // Check allow NULL @@ -572,10 +574,11 @@ static void _BuildMenuWithPills(NSMenu *menu,struct _cmpMap *map,size_t mapEntri [[[tableDataInstance statusValueForKey:@"Engine"] uppercaseString] isEqualToString:@"CSV"]) ? NO : YES]; } - // TEXT, BLOB, date, and GEOMETRY fields don't allow a length + // TEXT, BLOB, date, GEOMETRY and JSON fields don't allow a length else if ([[tableColumn identifier] isEqualToString:@"length"]) { [aCell setEnabled:([rowType hasSuffix:@"TEXT"] || - [rowType hasSuffix:@"BLOB"] || + [rowType hasSuffix:@"BLOB"] || + [rowType isEqualToString:@"JSON"] || ([fieldValidation isFieldTypeDate:rowType] && ![[tableDocumentInstance serverSupport] supportsFractionalSeconds] && ![rowType isEqualToString:@"YEAR"]) || [fieldValidation isFieldTypeGeometry:rowType]) ? NO : YES]; } @@ -659,6 +662,7 @@ static void _BuildMenuWithPills(NSMenu *menu,struct _cmpMap *map,size_t mapEntri NSInteger pos = [typeSuggestions indexOfObject:[cell stringValue]]; if(pos != NSNotFound) { [cell selectItemAtIndex:pos]; + [cell scrollItemAtIndexToTop:pos]; } //set up the help window to the right position diff --git a/Source/SPTableStructureLoading.m b/Source/SPTableStructureLoading.m index 57191b4b..b7f8e748 100644 --- a/Source/SPTableStructureLoading.m +++ b/Source/SPTableStructureLoading.m @@ -157,7 +157,15 @@ { NSString *type = [[[theField objectForKey:@"type"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString]; - if ([fieldValidation isFieldTypeString:type]) { + if([type isEqualToString:@"JSON"]) { + // MySQL 5.7 manual: + // "MySQL handles strings used in JSON context using the utf8mb4 character set and utf8mb4_bin collation. + // Strings in other character set are converted to utf8mb4 as necessary." + [theField setObject:@"utf8mb4" forKey:@"encodingName"]; + [theField setObject:@"utf8mb4_bin" forKey:@"collationName"]; + [theField setObject:@1 forKey:@"binary"]; + } + else if ([fieldValidation isFieldTypeString:type]) { // The MySQL 4.1 manual says: // // MySQL chooses the column character set and collation in the following manner: |