Skip to content

Commit 3e69d23

Browse files
committed
Add support for CURRENT_TIMESTAMP(n) in default/on update column of DATETIME/TIMESTAMP fields (part of #2315)
1 parent 47d9bf6 commit 3e69d23

10 files changed

+56
-26
lines changed

Source/SPConstants.h

+2
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,8 @@ extern NSString *SPBundleShellVariableAllFunctions;
620620
extern NSString *SPBundleShellVariableAllViews;
621621
extern NSString *SPBundleShellVariableAllTables;
622622

623+
extern NSString *SPCurrentTimestampPattern;
624+
623625
typedef NS_ENUM(NSInteger, SPBundleRedirectAction) {
624626
SPBundleRedirectActionNone = 200,
625627
SPBundleRedirectActionReplaceSection = 201,

Source/SPConstants.m

+5
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,11 @@
431431
NSString *SPBundleShellVariableSelectedTextRange = @"SP_SELECTED_TEXT_RANGE";
432432
NSString *SPBundleShellVariableUsedQueryForTable = @"SP_USED_QUERY_FOR_TABLE";
433433

434+
#define OWS @"\\s*" /* optional whitespace */
435+
// CURRENT_TIMESTAMP [ ( [n] ) ]
436+
NSString *SPCurrentTimestampPattern = (@"^" OWS @"CURRENT_TIMESTAMP" @"(?:" OWS @"\\(" OWS @"(\\d*)" OWS @"\\)" @")?" OWS @"$");
437+
#undef OWS
438+
434439
// URL scheme
435440
NSString *SPURLSchemeQueryInputPathHeader = @"/tmp/SP_QUERY_";
436441
NSString *SPURLSchemeQueryResultPathHeader = @"/tmp/SP_QUERY_RESULT_";

Source/SPCustomQuery.m

+7-6
Original file line numberDiff line numberDiff line change
@@ -1996,22 +1996,23 @@ - (void)saveCellValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn
19961996
} else if ( [anObject isKindOfClass:[NSData class]] ) {
19971997
newObject = [mySQLConnection escapeAndQuoteData:anObject];
19981998
} else {
1999-
if ( [[anObject description] isEqualToString:@"CURRENT_TIMESTAMP"] ) {
2000-
newObject = @"CURRENT_TIMESTAMP";
1999+
NSString *desc = [anObject description];
2000+
if ( [desc isMatchedByRegex:SPCurrentTimestampPattern] ) {
2001+
newObject = desc;
20012002
} else if ([anObject isEqualToString:[prefs stringForKey:SPNullValue]]
20022003
|| (([columnTypeGroup isEqualToString:@"float"] || [columnTypeGroup isEqualToString:@"integer"] || [columnTypeGroup isEqualToString:@"date"])
2003-
&& [[anObject description] isEqualToString:@""]))
2004+
&& [desc isEqualToString:@""]))
20042005
{
20052006
newObject = @"NULL";
20062007
} else if ([columnTypeGroup isEqualToString:@"geometry"]) {
20072008
newObject = [(NSString*)anObject getGeomFromTextString];
20082009
} else if ([columnTypeGroup isEqualToString:@"bit"]) {
2009-
newObject = [NSString stringWithFormat:@"b'%@'", ((![[anObject description] length] || [[anObject description] isEqualToString:@"0"]) ? @"0" : [anObject description])];
2010+
newObject = [NSString stringWithFormat:@"b'%@'", ((![desc length] || [desc isEqualToString:@"0"]) ? @"0" : desc)];
20102011
} else if ([columnTypeGroup isEqualToString:@"date"]
2011-
&& [[anObject description] isEqualToString:@"NOW()"]) {
2012+
&& [desc isEqualToString:@"NOW()"]) {
20122013
newObject = @"NOW()";
20132014
} else {
2014-
newObject = [mySQLConnection escapeAndQuoteString:[anObject description]];
2015+
newObject = [mySQLConnection escapeAndQuoteString:desc];
20152016
}
20162017
}
20172018

Source/SPObjectAdditions.h

+5
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,9 @@
3535
*/
3636
- (BOOL)isNSNull;
3737

38+
/**
39+
* easier to read version of [array containsObject:x]
40+
*/
41+
- (BOOL)isInArray:(NSArray *)list;
42+
3843
@end

Source/SPObjectAdditions.m

+5
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ - (BOOL)isNSNull
4646
return (self == null);
4747
}
4848

49+
- (BOOL)isInArray:(NSArray *)list
50+
{
51+
return [list containsObject:self];
52+
}
53+
4954
- (void)_scrollViewDidChangeBounds:(id)obj
5055
{
5156
NSMutableString *msg = [NSMutableString string];

Source/SPSQLExporter.m

+2-2
Original file line numberDiff line numberDiff line change
@@ -884,8 +884,8 @@ - (NSString *)_createViewPlaceholderSyntaxForView:(NSString *)viewName
884884
[fieldString appendString:@" DEFAULT NULL"];
885885
}
886886
}
887-
else if (([[column objectForKey:@"type"] isEqualToString:@"TIMESTAMP"] || [[column objectForKey:@"type"] isEqualToString:@"DATETIME"]) && [column objectForKey:@"default"] != [NSNull null] && [[[column objectForKey:@"default"] uppercaseString] isEqualToString:@"CURRENT_TIMESTAMP"]) {
888-
[fieldString appendString:@" DEFAULT CURRENT_TIMESTAMP"];
887+
else if (([[column objectForKey:@"type"] isInArray:@[@"TIMESTAMP",@"DATETIME"]]) && [[column objectForKey:@"default"] isMatchedByRegex:SPCurrentTimestampPattern]) {
888+
[fieldString appendFormat:@" DEFAULT %@",[column objectForKey:@"default"]];
889889
}
890890
else {
891891
[fieldString appendFormat:@" DEFAULT %@", [connection escapeAndQuoteString:[column objectForKey:@"default"]]];

Source/SPTableContent.m

+12-11
Original file line numberDiff line numberDiff line change
@@ -2801,14 +2801,15 @@ - (BOOL)saveRowToTable
28012801
fieldValue = [mySQLConnection escapeAndQuoteData:rowObject];
28022802

28032803
} else {
2804-
if ([[rowObject description] isEqualToString:@"CURRENT_TIMESTAMP"]) {
2805-
fieldValue = @"CURRENT_TIMESTAMP";
2804+
NSString *desc = [rowObject description];
2805+
if ([desc isMatchedByRegex:SPCurrentTimestampPattern]) {
2806+
fieldValue = desc;
28062807
} else if ([fieldTypeGroup isEqualToString:@"bit"]) {
2807-
fieldValue = [NSString stringWithFormat:@"b'%@'", ((![[rowObject description] length] || [[rowObject description] isEqualToString:@"0"]) ? @"0" : [rowObject description])];
2808-
} else if ([fieldTypeGroup isEqualToString:@"date"] && [[rowObject description] isEqualToString:@"NOW()"]) {
2808+
fieldValue = [NSString stringWithFormat:@"b'%@'", ((![desc length] || [desc isEqualToString:@"0"]) ? @"0" : desc)];
2809+
} else if ([fieldTypeGroup isEqualToString:@"date"] && [desc isEqualToString:@"NOW()"]) {
28092810
fieldValue = @"NOW()";
28102811
} else {
2811-
fieldValue = [mySQLConnection escapeAndQuoteString:[rowObject description]];
2812+
fieldValue = [mySQLConnection escapeAndQuoteString:desc];
28122813
}
28132814
}
28142815
}
@@ -3426,19 +3427,19 @@ - (void)saveViewCellValue:(id)anObject forTableColumn:(NSTableColumn *)aTableCol
34263427
} else if ( [anObject isKindOfClass:[NSData class]] ) {
34273428
newObject = [mySQLConnection escapeAndQuoteData:anObject];
34283429
} else {
3429-
if ( [[anObject description] isEqualToString:@"CURRENT_TIMESTAMP"] ) {
3430-
newObject = @"CURRENT_TIMESTAMP";
3430+
NSString *desc = [anObject description];
3431+
if ( [desc isMatchedByRegex:SPCurrentTimestampPattern] ) {
3432+
newObject = desc;
34313433
} else if([anObject isEqualToString:[prefs stringForKey:SPNullValue]]) {
34323434
newObject = @"NULL";
34333435
} else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"geometry"]) {
34343436
newObject = [(NSString*)anObject getGeomFromTextString];
34353437
} else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"bit"]) {
3436-
newObject = [NSString stringWithFormat:@"b'%@'", ((![[anObject description] length] || [[anObject description] isEqualToString:@"0"]) ? @"0" : [anObject description])];
3437-
} else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"date"]
3438-
&& [[anObject description] isEqualToString:@"NOW()"]) {
3438+
newObject = [NSString stringWithFormat:@"b'%@'", ((![desc length] || [desc isEqualToString:@"0"]) ? @"0" : desc)];
3439+
} else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"date"] && [desc isEqualToString:@"NOW()"]) {
34393440
newObject = @"NOW()";
34403441
} else {
3441-
newObject = [mySQLConnection escapeAndQuoteString:[anObject description]];
3442+
newObject = [mySQLConnection escapeAndQuoteString:desc];
34423443
}
34433444
}
34443445

Source/SPTableData.m

+2-1
Original file line numberDiff line numberDiff line change
@@ -1359,7 +1359,8 @@ - (NSDictionary *) parseFieldDefinitionStringParts:(NSArray *)definitionParts
13591359
// Special timestamp/datetime case - Whether fields are set to update the current timestamp
13601360
} else if ([detailString isEqualToString:@"ON"] && (definitionPartsIndex + 2 < partsArrayLength)
13611361
&& [[NSArrayObjectAtIndex(definitionParts, definitionPartsIndex+1) uppercaseString] isEqualToString:@"UPDATE"]
1362-
&& [[NSArrayObjectAtIndex(definitionParts, definitionPartsIndex+2) uppercaseString] isEqualToString:@"CURRENT_TIMESTAMP"]) {
1362+
&& [NSArrayObjectAtIndex(definitionParts, definitionPartsIndex+2) isMatchedByRegex:SPCurrentTimestampPattern]) {
1363+
// mysql requires the CURRENT_TIMESTAMP(n) to be exactly the same as the column types length, so we don't need to keep it, we can just restore it later
13631364
[fieldDetails setValue:@YES forKey:@"onupdatetimestamp"];
13641365
definitionPartsIndex += 2;
13651366

Source/SPTableStructure.m

+7-3
Original file line numberDiff line numberDiff line change
@@ -974,12 +974,12 @@ - (NSString *)_buildPartialColumnDefinitionString:(NSDictionary *)theRow
974974
}
975975
}
976976
// Otherwise, if CURRENT_TIMESTAMP was specified for timestamps/datetimes, use that
977-
else if (([theRowType isEqualToString:@"TIMESTAMP"] || [theRowType isEqualToString:@"DATETIME"]) &&
978-
[(matches = [[[theRow objectForKey:@"default"] uppercaseString] captureComponentsMatchedByRegex:@"^\\s*CURRENT_TIMESTAMP(?:\\s*\\(\\s*(\\d+)\\s*\\))?\\s*$"]) count])
977+
else if ([theRowType isInArray:@[@"TIMESTAMP",@"DATETIME"]] &&
978+
[(matches = [[[theRow objectForKey:@"default"] uppercaseString] captureComponentsMatchedByRegex:SPCurrentTimestampPattern]) count])
979979
{
980980
[queryString appendString:@"\n DEFAULT CURRENT_TIMESTAMP"];
981981
NSString *userLen = [matches objectAtIndex:1];
982-
//mysql 5.6.4+ allows DATETIME(n) for fractional seconds, which in turn requires CURRENT_TIMESTAMP(n).
982+
// mysql 5.6.4+ allows DATETIME(n) for fractional seconds, which in turn requires CURRENT_TIMESTAMP(n) with the same n!
983983
// Also, if the user explicitly added one we should never ignore that.
984984
if([userLen length] || fieldDefIncludesLen) {
985985
[queryString appendFormat:@"(%@)",([userLen length]? userLen : [theRow objectForKey:@"length"])];
@@ -1003,6 +1003,10 @@ - (NSString *)_buildPartialColumnDefinitionString:(NSDictionary *)theRow
10031003

10041004
if ([theRowExtra length] && ![theRowExtra isEqualToString:@"NONE"]) {
10051005
[queryString appendFormat:@"\n %@", theRowExtra];
1006+
//fix our own default item if needed
1007+
if([theRowExtra isEqualToString:@"ON UPDATE CURRENT_TIMESTAMP"] && fieldDefIncludesLen) {
1008+
[queryString appendFormat:@"(%@)",[theRow objectForKey:@"length"]];
1009+
}
10061010
}
10071011
}
10081012

Source/SPTableStructureLoading.m

+9-3
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,15 @@ - (void)loadTable:(NSString *)aTable
227227
}
228228

229229
// For timestamps/datetime check to see whether "on update CURRENT_TIMESTAMP" and set Extra accordingly
230-
else if (([type isEqualToString:@"TIMESTAMP"] || [type isEqualToString:@"DATETIME"]) &&
231-
[[theField objectForKey:@"onupdatetimestamp"] integerValue]) {
232-
[theField setObject:@"on update CURRENT_TIMESTAMP" forKey:@"Extra"];
230+
else if ([type isInArray:@[@"TIMESTAMP",@"DATETIME"]] && [[theField objectForKey:@"onupdatetimestamp"] boolValue]) {
231+
NSString *ouct = @"on update CURRENT_TIMESTAMP";
232+
// restore a length parameter if the field has fractional seconds.
233+
// the parameter of current_timestamp MUST match the field's length in that case, so we can just 'guess' it.
234+
NSString *fieldLen = [theField objectForKey:@"length"];
235+
if([fieldLen length] && ![fieldLen isEqualToString:@"0"]) {
236+
ouct = [ouct stringByAppendingFormat:@"(%@)",fieldLen];
237+
}
238+
[theField setObject:ouct forKey:@"Extra"];
233239
}
234240
}
235241

0 commit comments

Comments
 (0)