-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Allows unclosed quote characters to exist in comments #4993
fix: Allows unclosed quote characters to exist in comments #4993
Conversation
quoteStart = -1; | ||
} | ||
} | ||
int commentInd = line.indexOf(COMMENT); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will thrown an NPE if the line is null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a null check to all methods? Objects.requireNonNull
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added for the public method. In general, I'm a little wary of littering these sort of null checks within internal classes, but we seem to do that in a lot of areas in the codebase.
for(int i = 0; line != null && i < line.length(); ++i) { | ||
if (quoteStart < 0 && isQuoteChar(line, i)) { | ||
quoteStart = i; | ||
} else if (quoteStart >= 0 && line.charAt(quoteStart) == line.charAt(i) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line.charAt(quoteStart) == line.charAt(i)
check if the char is a quote? Why not use isQuoteChar(line, i)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's so you can have multiple quote types that contain the others e.g. "This could be 'valid'" and 'This could be "valid"'.
Given that we're just doing 'this type of quote', I'll simplify it.
@Test | ||
public void shouldFindUnclosedQuote() { | ||
// Given: | ||
final String line = "some line 'this is in a comment"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The string is a bit misleading :P These are not in comments, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hah, copied a bit too many times. I changed them to make sense, I believe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @AlanConfluent ! LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @AlanConfluent,
It's been a long time single I worked on this code, so I'm very rusty. However, I've added a few comments and suggestions.
These kind of changes always make me nervous when I'm making them as there are just so many edge cases. Adding extensive test cases is my best advice: think of every combination and permutation and add a test case!
Finally, I believe the sql standard allows a single quote to be escaped with another single quote, i.e. 'fred''s thing'
should be treated as fred's thing
- I think you've missed this case in your code + tests.
@@ -47,6 +47,10 @@ public ParsedLine parse(final String line, final int cursor, final ParseContext | |||
return parsed; | |||
} | |||
|
|||
if (UnclosedQuoteChecker.isUnclosedQuote(line)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not thought too much about this, as its been a while since I've been in this code... but are you happy with being passed line
and not parsed.line()
, i.e. do you understand the difference? (I certainly can't remember of the top of my head! )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've read through the source code of DefaultParser
and parsed.line()
appears to be just returning the line
passed to it, so this should be fine. https://github.com/jline/jline3/blob/master/reader/src/main/java/org/jline/reader/impl/DefaultParser.java#L312
private UnclosedQuoteChecker() {} | ||
|
||
public static boolean isUnclosedQuote(final String line) { | ||
requireNonNull(line, "line"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fyi: no need to null check an argument you're not storing in your object state.
And I think if you remove this you should also remove the line != null
in the for
loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree about not littering the code with null checks. Usually you would do it at exposed API layers, but not for internal code, since you can trust callers to know the invariants. What's the motivation in differentiating between storing it as state vs not?
Removed the null check below in the for loop.
final String line = "some line 'this is in a quote"; | ||
|
||
// Then: | ||
Assert.assertTrue(UnclosedQuoteChecker.isUnclosedQuote(line)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: prefer hamcrest matchers (with static import) over junit asserts, as they are most expressive.
(though in this case, you're comparing bools, so there's not much in it!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, switched to hamcrest
import org.junit.Assert; | ||
import org.junit.Test; | ||
|
||
public class UnclosedQuoteCheckerTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some test cases please?
- where
--
appears _within a quoted string - where
--
appears after a quoted string is closed? - where a quoted string contains a single quote, e.g. 'this string''contains a single quote'
- some with double quotes in quoted strings just for the hell of it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to add support for the twoQuoteChars in a row. I added tests for all of the above and others. Thanks for coming up with some good cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @AlanConfluent,
It's been a long time single I worked on this code, so I'm very rusty. However, I've added a few comments and suggestions.
I agree that this code is a bit tricky which is probably why this bug has been around for a bit. Hopefully, I've tested it well.
These kind of changes always make me nervous when I'm making them as there are just so many edge cases. Adding extensive test cases is my best advice: think of every combination and permutation and add a test case!
I've added a number of tests, including those you suggested and others. I covered the edge cases I can think of, including combinations of escaping and quoting.
Finally, I believe the sql standard allows a single quote to be escaped with another single quote, i.e.
'fred''s thing'
should be treated asfred's thing
- I think you've missed this case in your code + tests.
I added the two quote case you mentioned. Thanks for pointing that out! I don't believe that was explicitly handled using the DefaultParser, but they effectively support it by just pairing up quotes. My code also worked without explicit support, but I think the explicit is clearer, so I've added that.
@@ -47,6 +47,10 @@ public ParsedLine parse(final String line, final int cursor, final ParseContext | |||
return parsed; | |||
} | |||
|
|||
if (UnclosedQuoteChecker.isUnclosedQuote(line)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've read through the source code of DefaultParser
and parsed.line()
appears to be just returning the line
passed to it, so this should be fine. https://github.com/jline/jline3/blob/master/reader/src/main/java/org/jline/reader/impl/DefaultParser.java#L312
private UnclosedQuoteChecker() {} | ||
|
||
public static boolean isUnclosedQuote(final String line) { | ||
requireNonNull(line, "line"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree about not littering the code with null checks. Usually you would do it at exposed API layers, but not for internal code, since you can trust callers to know the invariants. What's the motivation in differentiating between storing it as state vs not?
Removed the null check below in the for loop.
import org.junit.Assert; | ||
import org.junit.Test; | ||
|
||
public class UnclosedQuoteCheckerTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to add support for the twoQuoteChars in a row. I added tests for all of the above and others. Thanks for coming up with some good cases.
final String line = "some line 'this is in a quote"; | ||
|
||
// Then: | ||
Assert.assertTrue(UnclosedQuoteChecker.isUnclosedQuote(line)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, switched to hamcrest
55820d6
to
4393477
Compare
Description
This allows support for unclosed quotes in comments. e.g. "SELECT -- It's a comment ".
There's actually a lot of code now that reparses the same line. The better longer term solution is to have a single parser and not delegate to DefaultParser, but I wanted to avoid making this too complex, since this fixes a small bug.
#4075
Testing done
mvn package
Reviewer checklist