Skip to content

Commit 13df27f

Browse files
committed
feat(cli): add Fluss CLI module with comprehensive test coverage
Core Features: - Interactive SQL REPL shell for Fluss clusters - Command-line SQL execution with -e and -f options - ASCII table formatting for query results - Complex type support (ARRAY, MAP, ROW) - Connection configuration management - Warning suppression for clean output (FLUSS_CLI_SUPPRESS_WARNINGS) Test Coverage: - 114 unit tests covering all core functionality - Integration test script (fluss-cli-release-check.sh) with 35 test cases - All tests passing with 0 failures - Checkstyle: 0 violations - Apache RAT: all files licensed Integration: - Add fluss-cli module to root pom.xml - Package CLI JAR in fluss-dist distribution - Add CLI documentation to website
1 parent 1e1dc57 commit 13df27f

29 files changed

+6505
-0
lines changed

fluss-cli/pom.xml

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
-->
19+
<project xmlns="http://maven.apache.org/POM/4.0.0"
20+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
21+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
22+
<modelVersion>4.0.0</modelVersion>
23+
<parent>
24+
<groupId>org.apache.fluss</groupId>
25+
<artifactId>fluss</artifactId>
26+
<version>0.9-SNAPSHOT</version>
27+
</parent>
28+
29+
<artifactId>fluss-cli</artifactId>
30+
<name>Fluss : CLI</name>
31+
<packaging>jar</packaging>
32+
33+
<properties>
34+
<picocli.version>4.7.5</picocli.version>
35+
<calcite.version>1.37.0</calcite.version>
36+
<jline.version>3.29.0</jline.version>
37+
<jansi.version>2.4.1</jansi.version>
38+
</properties>
39+
40+
<dependencies>
41+
<!-- Fluss dependencies -->
42+
<dependency>
43+
<groupId>org.apache.fluss</groupId>
44+
<artifactId>fluss-client</artifactId>
45+
<version>${project.version}</version>
46+
</dependency>
47+
48+
<dependency>
49+
<groupId>org.apache.fluss</groupId>
50+
<artifactId>fluss-common</artifactId>
51+
<version>${project.version}</version>
52+
</dependency>
53+
54+
<!-- CLI Framework -->
55+
<dependency>
56+
<groupId>info.picocli</groupId>
57+
<artifactId>picocli</artifactId>
58+
<version>${picocli.version}</version>
59+
</dependency>
60+
61+
<!-- SQL Parser -->
62+
<dependency>
63+
<groupId>org.apache.calcite</groupId>
64+
<artifactId>calcite-core</artifactId>
65+
<version>${calcite.version}</version>
66+
</dependency>
67+
68+
<!-- SQL DDL Parser Extension -->
69+
<dependency>
70+
<groupId>org.apache.calcite</groupId>
71+
<artifactId>calcite-server</artifactId>
72+
<version>${calcite.version}</version>
73+
</dependency>
74+
75+
<!-- SQL Babel Parser (supports complex types like ARRAY, MAP, ROW) -->
76+
<dependency>
77+
<groupId>org.apache.calcite</groupId>
78+
<artifactId>calcite-babel</artifactId>
79+
<version>${calcite.version}</version>
80+
</dependency>
81+
82+
<!-- Flink SQL Parser (supports complex types in DDL) -->
83+
<dependency>
84+
<groupId>org.apache.flink</groupId>
85+
<artifactId>flink-sql-parser</artifactId>
86+
<version>1.20.3</version>
87+
</dependency>
88+
89+
90+
<!-- Interactive Console (REPL) -->
91+
<dependency>
92+
<groupId>org.jline</groupId>
93+
<artifactId>jline</artifactId>
94+
<version>${jline.version}</version>
95+
</dependency>
96+
97+
<dependency>
98+
<groupId>org.jline</groupId>
99+
<artifactId>jline-reader</artifactId>
100+
<version>${jline.version}</version>
101+
</dependency>
102+
103+
<dependency>
104+
<groupId>org.jline</groupId>
105+
<artifactId>jline-terminal</artifactId>
106+
<version>${jline.version}</version>
107+
</dependency>
108+
109+
<!-- ANSI color support -->
110+
<dependency>
111+
<groupId>org.fusesource.jansi</groupId>
112+
<artifactId>jansi</artifactId>
113+
<version>${jansi.version}</version>
114+
</dependency>
115+
116+
<!-- Logging -->
117+
<dependency>
118+
<groupId>org.apache.logging.log4j</groupId>
119+
<artifactId>log4j-slf4j-impl</artifactId>
120+
<version>${log4j.version}</version>
121+
</dependency>
122+
123+
<dependency>
124+
<groupId>org.apache.logging.log4j</groupId>
125+
<artifactId>log4j-api</artifactId>
126+
<version>${log4j.version}</version>
127+
</dependency>
128+
129+
<dependency>
130+
<groupId>org.apache.logging.log4j</groupId>
131+
<artifactId>log4j-core</artifactId>
132+
<version>${log4j.version}</version>
133+
</dependency>
134+
135+
<!-- Test dependencies -->
136+
<dependency>
137+
<groupId>org.apache.fluss</groupId>
138+
<artifactId>fluss-test-utils</artifactId>
139+
</dependency>
140+
141+
<dependency>
142+
<groupId>org.apache.fluss</groupId>
143+
<artifactId>fluss-server</artifactId>
144+
<version>${project.version}</version>
145+
<scope>test</scope>
146+
</dependency>
147+
148+
<dependency>
149+
<groupId>org.apache.fluss</groupId>
150+
<artifactId>fluss-server</artifactId>
151+
<version>${project.version}</version>
152+
<type>test-jar</type>
153+
<scope>test</scope>
154+
</dependency>
155+
</dependencies>
156+
157+
<build>
158+
<plugins>
159+
<!-- Compiler Plugin -->
160+
<plugin>
161+
<groupId>org.apache.maven.plugins</groupId>
162+
<artifactId>maven-compiler-plugin</artifactId>
163+
<configuration>
164+
<source>${target.java.version}</source>
165+
<target>${target.java.version}</target>
166+
</configuration>
167+
</plugin>
168+
169+
<!-- Shade Plugin to create executable JAR -->
170+
<plugin>
171+
<groupId>org.apache.maven.plugins</groupId>
172+
<artifactId>maven-shade-plugin</artifactId>
173+
<executions>
174+
<execution>
175+
<id>shade-fluss-cli</id>
176+
<phase>package</phase>
177+
<goals>
178+
<goal>shade</goal>
179+
</goals>
180+
<configuration>
181+
<shadedArtifactAttached>false</shadedArtifactAttached>
182+
<createDependencyReducedPom>true</createDependencyReducedPom>
183+
<artifactSet>
184+
<includes>
185+
<include>*:*</include>
186+
</includes>
187+
<excludes>
188+
<exclude>org.apache.logging.log4j:log4j-slf4j-impl</exclude>
189+
</excludes>
190+
</artifactSet>
191+
<transformers>
192+
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
193+
<mainClass>org.apache.fluss.cli.FlussCliMain</mainClass>
194+
</transformer>
195+
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
196+
</transformers>
197+
<filters>
198+
<filter>
199+
<artifact>*:*</artifact>
200+
<excludes>
201+
<exclude>META-INF/*.SF</exclude>
202+
<exclude>META-INF/*.DSA</exclude>
203+
<exclude>META-INF/*.RSA</exclude>
204+
<exclude>META-INF/NOTICE</exclude>
205+
<exclude>META-INF/NOTICE.txt</exclude>
206+
<exclude>META-INF/LICENSE</exclude>
207+
<exclude>META-INF/LICENSE.txt</exclude>
208+
<exclude>META-INF/maven/**</exclude>
209+
<exclude>log4j.properties</exclude>
210+
<exclude>log4j2.properties</exclude>
211+
</excludes>
212+
</filter>
213+
</filters>
214+
</configuration>
215+
</execution>
216+
</executions>
217+
</plugin>
218+
219+
<plugin>
220+
<groupId>org.apache.maven.plugins</groupId>
221+
<artifactId>maven-resources-plugin</artifactId>
222+
<executions>
223+
<execution>
224+
<id>sync-cli-jar</id>
225+
<phase>package</phase>
226+
<goals>
227+
<goal>copy-resources</goal>
228+
</goals>
229+
<configuration>
230+
<outputDirectory>${project.basedir}/../build-target/lib</outputDirectory>
231+
<resources>
232+
<resource>
233+
<directory>${project.build.directory}</directory>
234+
<includes>
235+
<include>fluss-cli-${project.version}.jar</include>
236+
</includes>
237+
</resource>
238+
</resources>
239+
<overwrite>true</overwrite>
240+
</configuration>
241+
</execution>
242+
</executions>
243+
</plugin>
244+
</plugins>
245+
</build>
246+
</project>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.fluss.cli;
19+
20+
import org.apache.fluss.cli.command.SqlCommand;
21+
22+
import picocli.CommandLine;
23+
import picocli.CommandLine.Command;
24+
import picocli.CommandLine.HelpCommand;
25+
26+
/** Main entry point for the Fluss CLI tool. */
27+
@Command(
28+
name = "fluss-cli",
29+
description = "Fluss Command Line Interface",
30+
mixinStandardHelpOptions = true,
31+
version = "Fluss CLI 0.9-SNAPSHOT",
32+
subcommands = {SqlCommand.class, HelpCommand.class})
33+
public class FlussCliMain {
34+
35+
public static void main(String[] args) {
36+
int exitCode = new CommandLine(new FlussCliMain()).execute(args);
37+
System.exit(exitCode);
38+
}
39+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.fluss.cli.command;
19+
20+
import org.apache.fluss.cli.config.ConnectionConfig;
21+
import org.apache.fluss.cli.config.ConnectionManager;
22+
import org.apache.fluss.cli.repl.ReplShell;
23+
import org.apache.fluss.cli.sql.SqlExecutor;
24+
25+
import picocli.CommandLine.Command;
26+
import picocli.CommandLine.Option;
27+
import picocli.CommandLine.Parameters;
28+
29+
import java.io.File;
30+
import java.io.PrintWriter;
31+
import java.nio.file.Files;
32+
import java.util.concurrent.Callable;
33+
34+
/** Command for executing SQL statements. */
35+
@Command(name = "sql", description = "Execute SQL commands against Fluss cluster")
36+
public class SqlCommand implements Callable<Integer> {
37+
38+
@Option(
39+
names = {"-b", "--bootstrap-servers"},
40+
description = "Fluss bootstrap servers (host:port,host:port)",
41+
required = true)
42+
private String bootstrapServers;
43+
44+
@Option(
45+
names = {"-f", "--file"},
46+
description = "Execute SQL from file")
47+
private File sqlFile;
48+
49+
@Option(
50+
names = {"-e", "--execute"},
51+
description = "Execute SQL statement directly")
52+
private String sqlStatement;
53+
54+
@Option(
55+
names = {"-c", "--config"},
56+
description = "Configuration properties file")
57+
private File configFile;
58+
59+
@Parameters(description = "SQL statements to execute", arity = "0..1")
60+
private String sqlFromArgs;
61+
62+
@Override
63+
public Integer call() throws Exception {
64+
ConnectionConfig connectionConfig;
65+
66+
if (configFile != null) {
67+
connectionConfig = new ConnectionConfig(configFile);
68+
} else {
69+
connectionConfig = new ConnectionConfig(bootstrapServers);
70+
}
71+
72+
try (ConnectionManager connectionManager = new ConnectionManager(connectionConfig)) {
73+
PrintWriter out = new PrintWriter(System.out, true);
74+
SqlExecutor executor = new SqlExecutor(connectionManager, out);
75+
76+
if (sqlFile != null) {
77+
String sql = Files.readString(sqlFile.toPath());
78+
executor.executeSql(sql);
79+
} else if (sqlStatement != null) {
80+
executor.executeSql(sqlStatement);
81+
} else if (sqlFromArgs != null) {
82+
executor.executeSql(sqlFromArgs);
83+
} else {
84+
ReplShell repl = new ReplShell(executor);
85+
repl.run();
86+
}
87+
88+
return 0;
89+
} catch (Exception e) {
90+
System.err.println("Error: " + e.getMessage());
91+
e.printStackTrace();
92+
return 1;
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)