-
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
Handle 'RUN SCRIPT' directly in CLI, rather than post it to KSQL. #2331
Handle 'RUN SCRIPT' directly in CLI, rather than post it to KSQL. #2331
Conversation
- support for CLI commands that take multiple arguments. - Refactor CLI commands - printHelp is now just getHelpMessage - console.flush is handled in one place - the console itself. - CLI commands take suppliers / consumers etc, rather than whole `Console` reference - decoupling the code
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 a quick pass on the PR and notices that we are not accepting SET
and UNSET
commands in this version of RUN SCRIPT
, so this would not be consistent with the current behavior. In the current behavior, you can have SET
or UNSET
commands in the script and the engine will honor them. In this PR, the statement fails if we have SET
or UNSET
commands.
@big-andy-coates What would be your suggestion on handling SET
and UNSET
in this version of RUN SCRIPT
?
@hjafarpour Ah.... interesting. I thought the rest api handled SET/UNSET correctly. If that is not the case, then not sure how to handle SET/UNSET. Any suggestions welcome. Otherwise, I'll have a think. I guess we just fix the rest api to handle this right!?!? So I guess the same is true if someone was to fire off a multi-line statement to the rest api like: SET 'some.prop'='some.value';
CREATE TABLE t1 ....
UNSET 'some.prop';
CREATE TABLE t2 ... |
and add test
As discussed with @hjafarpour, here's the PR to allow the rest-api to support the SET / UNSET commands, (scoped to the single request), needed to match the functionality currently available in the server side run script, which would otherwise be lost with this change. |
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.
Thanks @big-andy-coates . LGTM with couple of minor comments.
One concern is the conflict between the methods in CmdLineUtil
class and our parser. I couldn't find an example that would result in a problem but we should keep an eye on this!
|
||
class RunScript implements CliSpecificCommand { | ||
|
||
private static final String HELP = "run <path_to_sql_file>:" + System.lineSeparator() |
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 should be "run script <path_to_sql_file>:"
.
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.
good catch!
console.writer().println(); | ||
console.writer().println( | ||
terminal.println(); | ||
terminal.println( | ||
" 1. The line is empty or entirely whitespace. In this" |
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.
@big-andy-coates I think this text should have been updated in your previous PR where you made '' for continuous lines optional. Let's update the text here.
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.
Yeah, it should indeed. I've updated.
@Test | ||
public void shouldGetHelp() { | ||
assertThat(cmd.getHelpMessage(), is( | ||
"run <path_to_sql_file>:" + System.lineSeparator() |
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.
Should be run script ...
.
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.
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 really like the gist of this change.
I haven't looked through everything since it is pretty meaty and (as the resident newbie) I still need to wrap my head on how things fit in to the bigger picture, but here's a first stab at a review (mostly small things)!
Obviously everything is a suggestion, feel free to ignore any comment :)
ksql-cli/src/main/java/io/confluent/ksql/cli/console/Console.java
Outdated
Show resolved
Hide resolved
ksql-cli/src/main/java/io/confluent/ksql/cli/console/cmd/CliSpecificCommand.java
Show resolved
Hide resolved
ksql-cli/src/main/java/io/confluent/ksql/cli/console/cmd/History.java
Outdated
Show resolved
Hide resolved
ksql-cli/src/main/java/io/confluent/ksql/cli/console/cmd/CliCmdUtil.java
Outdated
Show resolved
Hide resolved
} | ||
} | ||
|
||
private static final class QuoteRemover { |
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 think that this and WhitespaceParser
are both sufficiently complex to justify dedicated classes (and corresponding unit tests). I still need to go through and review them, but I'll do that with a fresh pair of eyes tomorrow morning :D
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.
IMHO they are implementation details of the methods that use them, and they are tested extensively through those methods.
I'm not saying they've not got bugs ;) . Just that we gain nothing by promoting these to top level classes and testing them independently of the only place they are intended to be used.
Having said that, I'm not particularly a fan of these classes. Raw pattern matching isn't ideal. So if you can suggest a better way to do this... I'm all ears!
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.
(''[^'\s]*)|'(?<esc>([^']|'')*)'|(\S+)
This (kinda crazy) Regex seems to do the job pretty well, though at this point I'm not sure if I prefer this "magic" over the original:
String text = "''t0 t1 'quoted string' 'quoted '' string'";
final String NOT_DOUBLE = "''[^'\\s]*";
final String ESCAPED = "([^']|'')*";
final String NOT_WHITESPACE = "\\S+";
String regex = String.format("(%s)|'(?<esc>%s)'|(%s)", NOT_DOUBLE, ESCAPED, NOT_WHITESPACE);
Matcher m = Pattern.compile(regex).matcher(text);
while (m.find()) {
System.out.println(ObjectUtils.defaultIfNull(m.group("esc"), m.group()).replace("''","'"));
}
This outputs
't0
t1
quoted string
quoted ' string
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.
Interesting!
Does the above regex work for all the test cases? I didn't think it was possible to match quotes with regex in the presence of escaped quotes.
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 have not checked - after you push the current code maybe I'll play around with it a little bit, time permitting.
Thanks for the review @hjafarpour and welcome @agavra! |
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.
Took a pass through. Will do one more tomorrow.
|
||
private static String loadScript(final String filePath) { | ||
try { | ||
return Files.readLines(new File(filePath), StandardCharsets.UTF_8).stream() |
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.
Why not just use Files.toString here?
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.
Because its deprecated :)
Mind you, I wasn't intending to use Guava here at all, so you highlighting this has at least sorted that. It now uses Java NIO Files
.
terminal | ||
.registerCliSpecificCommand(new RemoteServerSpecificCommand(restClient, terminal.writer())); | ||
.registerCliSpecificCommand(new RemoteServerSpecificCommand(restClient)); |
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.
Move this line into registerDefaultCommands?
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.
} | ||
|
||
@Override | ||
public void makeKsqlRequest(final String requestBody) { |
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: rename to something like statementText or statements - requestBody implies the http request body
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
|
||
console.registerCliSpecificCommand(new Clear(console)); | ||
console.registerCliSpecificCommand(new Clear(console::clearScreen)); |
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.
Looks like we've dropped the call to flush() after clearScreen()
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.
Console now explicitly flushes after any command has run. So individual commands don't need to worry about it.
} | ||
|
||
@Override | ||
public void execute(final String commandStrippedLine) { | ||
console.printHistory(); | ||
console.flush(); |
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 flush call gets dropped.
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.
As above.
pos--; | ||
} | ||
|
||
return pos < 0 ? "" : s.substring(0, pos + 1); |
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.
you can drop the condition - substring(0, 0) should return ""
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.
Code dropped.
private boolean inQuotes; | ||
private boolean inWhitespace; | ||
private String input; | ||
private Builder<String> output; |
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: can we use the full name here (ImmutableList.Builder)? I had to read this a few times before I realized its not building a String.
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.
* <ul> | ||
* <li>{@code "t0 t1"} becomes {@code ["t0", "t1"]}</li> | ||
* <li>{@code "'quoted string'"} becomes {@code ["quoted string"]}</li> | ||
* <li>{@code "'quoted'connected'quoted'"} becomes {@code ["quotedconnectedquoted"]}</li> |
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 think this example should not have the quotes stripped.
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.
If you follow the pattern of how bash shell strips quotes then this should work.
Plus its such an edge case, I don't really want to add special code to not strip here.
OK?
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 meant you should fix the example in the comment. The result for 'quoted'connected'quoted'
is 'quoted'connected'quoted'
, right? This function isn't stripping quotes.
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.
Ah, yeah, you're right. I've updated and also added tests covering the javadocs of both the split and strip functions.
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.
LGTM, though I'll defer the second +1 to Rohan since he's already reviewing and has better context
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.
LGTM, modulo one inline issue around the docstring for splitByUnquotedWhitespace
Thanks all for the reviews! |
Description
This is the CLI side of the work for #2179.
Fixed #1031 by deprecating the run script functionality.
The CLI can now handle
RUN SCRIPT
commands by loading the contents of the script and posting it to KSQL as a request containing multiple lines of SQL, rather than as a special 'run script' command.i.e. this change means the CLI is no longer reliant on the server's 'run script' functionality. Meaning it can be removed in a later PR. The CLI now handles run script by loading the script content and posting it to the server as a single multi-statement request. This means the rest-server will parse and 'tryExecute' the set of statements, (rather than use the server side run-script functionality which dumbly posts the script contents to the command topic without any validation). If the tryexecute succeeds, then each statement will sequentially be posted to the command topic.
While I was working on a new CLI command I took the opportunity to refactor the interface to decouple it more from the
Console
.Supplier
,Consumer
andRunnable
interfaces in their constructors. These are wired back to the necessary methods on theConsole
in the true CLI app.The PR looks to maintain backwards compatibility with existing run script functionality. So you can still type:
Though the trailing semi-colon is optional.
With this change,
RUN SCRIPT
commands entered in the CLI are no longer written to the command topic as aRUN SCRIPT
command. Instead, the server will parse the contents of the script, validate, and then write each executable statement to the command topic separately.There will be follow on PRs to improve the server side handling of multi-statement requests.
Testing done
Manual testing of all existing commands and the new command.
Reviewer checklist