Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,30 @@ private CalciteSqlParser() {
private static final Pattern OPTIONS_REGEX_PATTEN =
Pattern.compile("option\\s*\\(([^\\)]+)\\)", Pattern.CASE_INSENSITIVE);

/**
* Checks for the presence of semicolon in the sql query and modifies the query accordingly
*
* @param sql sql query
* @return sql query without semicolons
*
*/
private static String removeTerminatingSemicolon(String sql) {
// trim all the leading and trailing whitespaces
sql = sql.trim();
int sqlLength = sql.length();

// Terminate the semicolon only if the last character of the query is semicolon
if (sql.charAt(sqlLength - 1) == ';') {
return sql.substring(0, sqlLength - 1);
}
return sql;
}

public static PinotQuery compileToPinotQuery(String sql)
throws SqlCompilationException {
// Removes the terminating semicolon if any
sql = removeTerminatingSemicolon(sql);

// Extract OPTION statements from sql as Calcite Parser doesn't parse it.
List<String> options = extractOptionsFromSql(sql);
if (!options.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2387,4 +2387,71 @@ private void testSupportedDistinctQuery(String query) {
PinotQuery pinotQuery = CalciteSqlParser.compileToPinotQuery(query);
Assert.assertNotNull(pinotQuery);
}

@Test
public void testQueryWithSemicolon() {
String sql;
PinotQuery pinotQuery;
sql = "SELECT col1, col2 FROM foo;";
pinotQuery = CalciteSqlParser.compileToPinotQuery(sql);
Assert.assertEquals(pinotQuery.getSelectListSize(), 2);
Assert.assertEquals(pinotQuery.getSelectList().get(0).getIdentifier().getName(), "col1");
Assert.assertEquals(pinotQuery.getSelectList().get(1).getIdentifier().getName(), "col2");

// Query having extra white spaces before the semicolon
sql = "SELECT col1, col2 FROM foo ;";
pinotQuery = CalciteSqlParser.compileToPinotQuery(sql);
Assert.assertEquals(pinotQuery.getSelectListSize(), 2);
Assert.assertEquals(pinotQuery.getSelectList().get(0).getIdentifier().getName(), "col1");
Assert.assertEquals(pinotQuery.getSelectList().get(1).getIdentifier().getName(), "col2");

// Query having leading and trailing whitespaces
sql = " SELECT col1, col2 FROM foo; ";
pinotQuery = CalciteSqlParser.compileToPinotQuery(sql);
Assert.assertEquals(pinotQuery.getSelectListSize(), 2);
Assert.assertEquals(pinotQuery.getSelectList().get(0).getIdentifier().getName(), "col1");
Assert.assertEquals(pinotQuery.getSelectList().get(1).getIdentifier().getName(), "col2");

sql = "SELECT col1, count(*) FROM foo group by col1;";
pinotQuery = CalciteSqlParser.compileToPinotQuery(sql);
Assert.assertEquals(pinotQuery.getSelectListSize(), 2);
Assert.assertEquals(pinotQuery.getSelectList().get(0).getIdentifier().getName(), "col1");
Assert.assertEquals(pinotQuery.getGroupByListSize(), 1);
Assert.assertEquals(pinotQuery.getGroupByList().get(0).getIdentifier().getName(), "col1");
Assert.assertEquals(pinotQuery.getGroupByList().get(0).getIdentifier().getName(), "col1");

// Check for Option SQL Query
sql = "SELECT col1, count(*) FROM foo group by col1 option(skipUpsert=true);";
pinotQuery = CalciteSqlParser.compileToPinotQuery(sql);
Assert.assertEquals(pinotQuery.getQueryOptionsSize(), 1);
Assert.assertTrue(pinotQuery.getQueryOptions().containsKey("skipUpsert"));

// Check for the query where the literal has semicolon
sql = "select col1, count(*) from foo where col1 = 'x;y' option(skipUpsert=true);";
pinotQuery = CalciteSqlParser.compileToPinotQuery(sql);
Assert.assertEquals(pinotQuery.getQueryOptionsSize(), 1);
Assert.assertTrue(pinotQuery.getQueryOptions().containsKey("skipUpsert"));
}

@Test
public void testInvalidQueryWithSemicolon() {
Assert.expectThrows(SqlCompilationException.class,
() -> CalciteSqlParser.compileToPinotQuery(";"));

Assert.expectThrows(SqlCompilationException.class,
() -> CalciteSqlParser.compileToPinotQuery(";;;;"));

Assert.expectThrows(SqlCompilationException.class,
() -> CalciteSqlParser.compileToPinotQuery("SELECT col1, count(*) FROM foo GROUP BY ; col1"));

// Query having multiple SQL statements
Assert.expectThrows(SqlCompilationException.class,
() -> CalciteSqlParser.compileToPinotQuery("SELECT col1, count(*) FROM foo GROUP BY col1; SELECT col2,"
+ "count(*) FROM foo GROUP BY col2"));

// Query having multiple SQL statements with trailing and leading whitespaces
Assert.expectThrows(SqlCompilationException.class,
() -> CalciteSqlParser.compileToPinotQuery(" SELECT col1, count(*) FROM foo GROUP BY col1; "
+ "SELECT col2, count(*) FROM foo GROUP BY col2 "));
}
}