Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add support for CURRENT_TIMESTAMP(n) in default/on update column of D…
…ATETIME/TIMESTAMP fields (part of #2315)
  • Loading branch information
dmoagx committed Nov 4, 2015
1 parent 47d9bf6 commit 3e69d23
Show file tree
Hide file tree
Showing 10 changed files with 56 additions and 26 deletions.
2 changes: 2 additions & 0 deletions Source/SPConstants.h
Expand Up @@ -620,6 +620,8 @@ extern NSString *SPBundleShellVariableAllFunctions;
extern NSString *SPBundleShellVariableAllViews;
extern NSString *SPBundleShellVariableAllTables;

extern NSString *SPCurrentTimestampPattern;

typedef NS_ENUM(NSInteger, SPBundleRedirectAction) {
SPBundleRedirectActionNone = 200,
SPBundleRedirectActionReplaceSection = 201,
Expand Down
5 changes: 5 additions & 0 deletions Source/SPConstants.m
Expand Up @@ -431,6 +431,11 @@
NSString *SPBundleShellVariableSelectedTextRange = @"SP_SELECTED_TEXT_RANGE";
NSString *SPBundleShellVariableUsedQueryForTable = @"SP_USED_QUERY_FOR_TABLE";

#define OWS @"\\s*" /* optional whitespace */
// CURRENT_TIMESTAMP [ ( [n] ) ]
NSString *SPCurrentTimestampPattern = (@"^" OWS @"CURRENT_TIMESTAMP" @"(?:" OWS @"\\(" OWS @"(\\d*)" OWS @"\\)" @")?" OWS @"$");
#undef OWS

// URL scheme
NSString *SPURLSchemeQueryInputPathHeader = @"/tmp/SP_QUERY_";
NSString *SPURLSchemeQueryResultPathHeader = @"/tmp/SP_QUERY_RESULT_";
Expand Down
13 changes: 7 additions & 6 deletions Source/SPCustomQuery.m
Expand Up @@ -1996,22 +1996,23 @@ - (void)saveCellValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn
} else if ( [anObject isKindOfClass:[NSData class]] ) {
newObject = [mySQLConnection escapeAndQuoteData:anObject];
} else {
if ( [[anObject description] isEqualToString:@"CURRENT_TIMESTAMP"] ) {
newObject = @"CURRENT_TIMESTAMP";
NSString *desc = [anObject description];
if ( [desc isMatchedByRegex:SPCurrentTimestampPattern] ) {
newObject = desc;
} else if ([anObject isEqualToString:[prefs stringForKey:SPNullValue]]
|| (([columnTypeGroup isEqualToString:@"float"] || [columnTypeGroup isEqualToString:@"integer"] || [columnTypeGroup isEqualToString:@"date"])
&& [[anObject description] isEqualToString:@""]))
&& [desc isEqualToString:@""]))
{
newObject = @"NULL";
} else if ([columnTypeGroup isEqualToString:@"geometry"]) {
newObject = [(NSString*)anObject getGeomFromTextString];
} else if ([columnTypeGroup isEqualToString:@"bit"]) {
newObject = [NSString stringWithFormat:@"b'%@'", ((![[anObject description] length] || [[anObject description] isEqualToString:@"0"]) ? @"0" : [anObject description])];
newObject = [NSString stringWithFormat:@"b'%@'", ((![desc length] || [desc isEqualToString:@"0"]) ? @"0" : desc)];
} else if ([columnTypeGroup isEqualToString:@"date"]
&& [[anObject description] isEqualToString:@"NOW()"]) {
&& [desc isEqualToString:@"NOW()"]) {
newObject = @"NOW()";
} else {
newObject = [mySQLConnection escapeAndQuoteString:[anObject description]];
newObject = [mySQLConnection escapeAndQuoteString:desc];
}
}

Expand Down
5 changes: 5 additions & 0 deletions Source/SPObjectAdditions.h
Expand Up @@ -35,4 +35,9 @@
*/
- (BOOL)isNSNull;

/**
* easier to read version of [array containsObject:x]
*/
- (BOOL)isInArray:(NSArray *)list;

@end
5 changes: 5 additions & 0 deletions Source/SPObjectAdditions.m
Expand Up @@ -46,6 +46,11 @@ - (BOOL)isNSNull
return (self == null);
}

- (BOOL)isInArray:(NSArray *)list
{
return [list containsObject:self];
}

- (void)_scrollViewDidChangeBounds:(id)obj
{
NSMutableString *msg = [NSMutableString string];
Expand Down
4 changes: 2 additions & 2 deletions Source/SPSQLExporter.m
Expand Up @@ -884,8 +884,8 @@ - (NSString *)_createViewPlaceholderSyntaxForView:(NSString *)viewName
[fieldString appendString:@" DEFAULT NULL"];
}
}
else if (([[column objectForKey:@"type"] isEqualToString:@"TIMESTAMP"] || [[column objectForKey:@"type"] isEqualToString:@"DATETIME"]) && [column objectForKey:@"default"] != [NSNull null] && [[[column objectForKey:@"default"] uppercaseString] isEqualToString:@"CURRENT_TIMESTAMP"]) {
[fieldString appendString:@" DEFAULT CURRENT_TIMESTAMP"];
else if (([[column objectForKey:@"type"] isInArray:@[@"TIMESTAMP",@"DATETIME"]]) && [[column objectForKey:@"default"] isMatchedByRegex:SPCurrentTimestampPattern]) {
[fieldString appendFormat:@" DEFAULT %@",[column objectForKey:@"default"]];
}
else {
[fieldString appendFormat:@" DEFAULT %@", [connection escapeAndQuoteString:[column objectForKey:@"default"]]];
Expand Down
23 changes: 12 additions & 11 deletions Source/SPTableContent.m
Expand Up @@ -2801,14 +2801,15 @@ - (BOOL)saveRowToTable
fieldValue = [mySQLConnection escapeAndQuoteData:rowObject];

} else {
if ([[rowObject description] isEqualToString:@"CURRENT_TIMESTAMP"]) {
fieldValue = @"CURRENT_TIMESTAMP";
NSString *desc = [rowObject description];
if ([desc isMatchedByRegex:SPCurrentTimestampPattern]) {
fieldValue = desc;
} else if ([fieldTypeGroup isEqualToString:@"bit"]) {
fieldValue = [NSString stringWithFormat:@"b'%@'", ((![[rowObject description] length] || [[rowObject description] isEqualToString:@"0"]) ? @"0" : [rowObject description])];
} else if ([fieldTypeGroup isEqualToString:@"date"] && [[rowObject description] isEqualToString:@"NOW()"]) {
fieldValue = [NSString stringWithFormat:@"b'%@'", ((![desc length] || [desc isEqualToString:@"0"]) ? @"0" : desc)];
} else if ([fieldTypeGroup isEqualToString:@"date"] && [desc isEqualToString:@"NOW()"]) {
fieldValue = @"NOW()";
} else {
fieldValue = [mySQLConnection escapeAndQuoteString:[rowObject description]];
fieldValue = [mySQLConnection escapeAndQuoteString:desc];
}
}
}
Expand Down Expand Up @@ -3426,19 +3427,19 @@ - (void)saveViewCellValue:(id)anObject forTableColumn:(NSTableColumn *)aTableCol
} else if ( [anObject isKindOfClass:[NSData class]] ) {
newObject = [mySQLConnection escapeAndQuoteData:anObject];
} else {
if ( [[anObject description] isEqualToString:@"CURRENT_TIMESTAMP"] ) {
newObject = @"CURRENT_TIMESTAMP";
NSString *desc = [anObject description];
if ( [desc isMatchedByRegex:SPCurrentTimestampPattern] ) {
newObject = desc;
} else if([anObject isEqualToString:[prefs stringForKey:SPNullValue]]) {
newObject = @"NULL";
} else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"geometry"]) {
newObject = [(NSString*)anObject getGeomFromTextString];
} else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"bit"]) {
newObject = [NSString stringWithFormat:@"b'%@'", ((![[anObject description] length] || [[anObject description] isEqualToString:@"0"]) ? @"0" : [anObject description])];
} else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"date"]
&& [[anObject description] isEqualToString:@"NOW()"]) {
newObject = [NSString stringWithFormat:@"b'%@'", ((![desc length] || [desc isEqualToString:@"0"]) ? @"0" : desc)];
} else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"date"] && [desc isEqualToString:@"NOW()"]) {
newObject = @"NOW()";
} else {
newObject = [mySQLConnection escapeAndQuoteString:[anObject description]];
newObject = [mySQLConnection escapeAndQuoteString:desc];
}
}

Expand Down
3 changes: 2 additions & 1 deletion Source/SPTableData.m
Expand Up @@ -1359,7 +1359,8 @@ - (NSDictionary *) parseFieldDefinitionStringParts:(NSArray *)definitionParts
// Special timestamp/datetime case - Whether fields are set to update the current timestamp
} else if ([detailString isEqualToString:@"ON"] && (definitionPartsIndex + 2 < partsArrayLength)
&& [[NSArrayObjectAtIndex(definitionParts, definitionPartsIndex+1) uppercaseString] isEqualToString:@"UPDATE"]
&& [[NSArrayObjectAtIndex(definitionParts, definitionPartsIndex+2) uppercaseString] isEqualToString:@"CURRENT_TIMESTAMP"]) {
&& [NSArrayObjectAtIndex(definitionParts, definitionPartsIndex+2) isMatchedByRegex:SPCurrentTimestampPattern]) {
// 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
[fieldDetails setValue:@YES forKey:@"onupdatetimestamp"];
definitionPartsIndex += 2;

Expand Down
10 changes: 7 additions & 3 deletions Source/SPTableStructure.m
Expand Up @@ -974,12 +974,12 @@ - (NSString *)_buildPartialColumnDefinitionString:(NSDictionary *)theRow
}
}
// Otherwise, if CURRENT_TIMESTAMP was specified for timestamps/datetimes, use that
else if (([theRowType isEqualToString:@"TIMESTAMP"] || [theRowType isEqualToString:@"DATETIME"]) &&
[(matches = [[[theRow objectForKey:@"default"] uppercaseString] captureComponentsMatchedByRegex:@"^\\s*CURRENT_TIMESTAMP(?:\\s*\\(\\s*(\\d+)\\s*\\))?\\s*$"]) count])
else if ([theRowType isInArray:@[@"TIMESTAMP",@"DATETIME"]] &&
[(matches = [[[theRow objectForKey:@"default"] uppercaseString] captureComponentsMatchedByRegex:SPCurrentTimestampPattern]) count])
{
[queryString appendString:@"\n DEFAULT CURRENT_TIMESTAMP"];
NSString *userLen = [matches objectAtIndex:1];
//mysql 5.6.4+ allows DATETIME(n) for fractional seconds, which in turn requires CURRENT_TIMESTAMP(n).
// mysql 5.6.4+ allows DATETIME(n) for fractional seconds, which in turn requires CURRENT_TIMESTAMP(n) with the same n!
// Also, if the user explicitly added one we should never ignore that.
if([userLen length] || fieldDefIncludesLen) {
[queryString appendFormat:@"(%@)",([userLen length]? userLen : [theRow objectForKey:@"length"])];
Expand All @@ -1003,6 +1003,10 @@ - (NSString *)_buildPartialColumnDefinitionString:(NSDictionary *)theRow

if ([theRowExtra length] && ![theRowExtra isEqualToString:@"NONE"]) {
[queryString appendFormat:@"\n %@", theRowExtra];
//fix our own default item if needed
if([theRowExtra isEqualToString:@"ON UPDATE CURRENT_TIMESTAMP"] && fieldDefIncludesLen) {
[queryString appendFormat:@"(%@)",[theRow objectForKey:@"length"]];
}
}
}

Expand Down
12 changes: 9 additions & 3 deletions Source/SPTableStructureLoading.m
Expand Up @@ -227,9 +227,15 @@ - (void)loadTable:(NSString *)aTable
}

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

Expand Down

0 comments on commit 3e69d23

Please sign in to comment.