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
31 changes: 24 additions & 7 deletions query/src/sql/plan_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ use sqlparser::ast::Query;
use sqlparser::ast::Statement;
use sqlparser::ast::TableFactor;

use super::DfShowTables;
use crate::catalogs::catalog::Catalog;
use crate::functions::ContextFunction;
use crate::sessions::DatafuseQueryContextRef;
Expand Down Expand Up @@ -133,13 +134,29 @@ impl PlanParser {
DfStatement::ShowCreateTable(v) => self.sql_show_create_table_to_plan(v),

// TODO: support like and other filters in show queries
DfStatement::ShowTables(_) => self.build_from_sql(
format!(
"SELECT name FROM system.tables where database = '{}' ORDER BY database, name",
self.ctx.get_current_database()
)
.as_str(),
),
DfStatement::ShowTables(df) => {
let show_sql = match df {
DfShowTables::All => {
format!(
"SELECT name FROM system.tables where database = '{}' ORDER BY database, name",
self.ctx.get_current_database()
)
}
DfShowTables::Like(i) => {
format!(
"SELECT name FROM system.tables where database = '{}' AND name LIKE {} ORDER BY database, name",
self.ctx.get_current_database(), i,
)
}
DfShowTables::Where(e) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens to the following SQL?

SHOW TABLES WHERE 1 = 1  OR 1 = 1 

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens to the following SQL?

SHOW TABLES WHERE 1 = 1  OR 1 = 1 

It's ok, we don't care about it, just let it be any complex expr. The optimize / select executor will handle that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to MySQL standard,parse_expr can handle all of them: https://dev.mysql.com/doc/refman/5.7/en/expressions.html

Buf I didn't check the code in sqlparse-rs, so I cannot guarantee it works well.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May this change the behavior of the query?

After format:
SELECT name FROM system.tables where database = '{}' AND 1 = 1 OR 1 = 1 ORDER BY database, name

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May this change the behavior of the query?

After format:
SELECT name FROM system.tables where database = '{}' AND 1 = 1 OR 1 = 1 ORDER BY database, name

Got It. Sorry for misunderstanding this case! I will adding tests about it in stateless test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

evaluation proceeds left to right, with the exception that assignments evaluate right to left

According to https://dev.mysql.com/doc/refman/8.0/en/operator-precedence.html , it will not return all tables. But I think maybe it's proper to build sql like:

SELECT name FROM system.tables where database = '{}' AND (1 = 1 OR 1 = 1) ORDER BY database, name

@zhang2014 do you think it's ok?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhang2014 do you think it's ok?

It looks ok

format!(
"SELECT name FROM system.tables where database = '{}' AND ({}) ORDER BY database, name",
self.ctx.get_current_database(), e,
)
}
};
self.build_from_sql(show_sql.as_str())
}
DfStatement::ShowSettings(_) => self.build_from_sql("SELECT name FROM system.settings"),
DfStatement::ShowProcessList(_) => {
self.build_from_sql("SELECT * FROM system.processes")
Expand Down
15 changes: 14 additions & 1 deletion query/src/sql/sql_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,20 @@ impl<'a> DfParser<'a> {
self.parser.next_token();

if self.consume_token("TABLES") {
Ok(DfStatement::ShowTables(DfShowTables))
let tok = self.parser.next_token();
match &tok {
Token::EOF => Ok(DfStatement::ShowTables(DfShowTables::All)),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens to the following SQL?

SHOW TABLES  ; 
SHOW TABLES -- comment

@mapleFU mapleFU Aug 19, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add some tests and check if it's ok tonight.

Token::Word(w) => match w.keyword {
Keyword::LIKE => Ok(DfStatement::ShowTables(
DfShowTables::Like(self.parser.parse_identifier()?),
)),
Keyword::WHERE => Ok(DfStatement::ShowTables(
DfShowTables::Where(self.parser.parse_expr()?),
)),
_ => self.expected("like or where", tok),
},
_ => self.expected("like or where", tok),
}
} else if self.consume_token("DATABASES") {
Ok(DfStatement::ShowDatabases(DfShowDatabases))
} else if self.consume_token("SETTINGS") {
Expand Down
40 changes: 39 additions & 1 deletion query/src/sql/sql_parser_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,47 @@ mod tests {

#[test]
fn show_queries() -> Result<()> {
use sqlparser::dialect::GenericDialect;
use sqlparser::parser::Parser;
use sqlparser::tokenizer::Tokenizer;

// positive case
expect_parse_ok("SHOW TABLES", DfStatement::ShowTables(DfShowTables))?;
expect_parse_ok("SHOW TABLES", DfStatement::ShowTables(DfShowTables::All))?;
expect_parse_ok("SHOW SETTINGS", DfStatement::ShowSettings(DfShowSettings))?;
expect_parse_ok(
"SHOW TABLES LIKE 'aaa'",
DfStatement::ShowTables(DfShowTables::Like(Ident::with_quote('\'', "aaa"))),
)?;

expect_parse_ok(
"SHOW TABLES --comments should not in sql case1",
DfStatement::ShowTables(DfShowTables::All),
)?;

expect_parse_ok(
"SHOW TABLES LIKE 'aaa' --comments should not in sql case2",
DfStatement::ShowTables(DfShowTables::Like(Ident::with_quote('\'', "aaa"))),
)?;

let parse_sql_to_expr = |query_expr: &str| -> Expr {
let dialect = GenericDialect {};
let mut tokenizer = Tokenizer::new(&dialect, &query_expr);
let tokens = tokenizer.tokenize().unwrap();
let mut parser = Parser::new(tokens, &dialect);
return parser.parse_expr().unwrap();
};

expect_parse_ok(
"SHOW TABLES WHERE t LIKE 'aaa'",
DfStatement::ShowTables(DfShowTables::Where(parse_sql_to_expr("t LIKE 'aaa'"))),
)?;

expect_parse_ok(
"SHOW TABLES WHERE t LIKE 'aaa' AND t LIKE 'a%'",
DfStatement::ShowTables(DfShowTables::Where(parse_sql_to_expr(
"t LIKE 'aaa' AND t LIKE 'a%'",
))),
)?;

Ok(())
}
Expand Down
7 changes: 6 additions & 1 deletion query/src/sql/sql_statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,18 @@ use nom::character::complete::multispace0;
use nom::character::complete::multispace1;
use nom::IResult;
use sqlparser::ast::ColumnDef;
use sqlparser::ast::Expr;
use sqlparser::ast::Ident;
use sqlparser::ast::ObjectName;
use sqlparser::ast::SqlOption;
use sqlparser::ast::Statement as SQLStatement;

#[derive(Debug, Clone, PartialEq)]
pub struct DfShowTables;
pub enum DfShowTables {
All,
Like(Ident),
Where(Expr),
}

#[derive(Debug, Clone, PartialEq)]
pub struct DfShowDatabases;
Expand Down
15 changes: 14 additions & 1 deletion tests/suites/0_stateless/06_0000_show_queries.result
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
t
t1
t2
t3
t1
t2
t3
t2
t1
t2
t3
t1
t2
t3
t2
22 changes: 19 additions & 3 deletions tests/suites/0_stateless/06_0000_show_queries.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
DROP TABLE IF EXISTS t;
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
DROP TABLE IF EXISTS t3;

CREATE TABLE t1(c1 int) ENGINE = Null;
CREATE TABLE t2(c1 int) ENGINE = Null;
CREATE TABLE t3(c1 int) ENGINE = Null;

CREATE TABLE t(c1 int) ENGINE = Null;
SHOW TABLES;

DROP TABLE IF EXISTS t;
SHOW TABLES LIKE 't%';
SHOW TABLES LIKE 't2';
SHOW TABLES LIKE 't';

SHOW TABLES WHERE name LIKE 't%';
SHOW TABLES WHERE name = 't%' AND 1 = 0;
SHOW TABLES WHERE name = 't2' OR 1 = 1;
SHOW TABLES WHERE name = 't2' AND 1 = 1;

DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
DROP TABLE IF EXISTS t3;