diff options
author | Timothy Flynn <trflynn89@pm.me> | 2021-04-23 14:45:56 -0400 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-04-23 22:36:07 +0200 |
commit | 004025c3c4bfb3e9998e5c860700e357d70b41a4 (patch) | |
tree | 16d030a30b95f2dcb31912237dd43e12139d460e /Userland/Libraries/LibSQL | |
parent | cb943a2179cd8e33983eb086e05def3e3e60bc28 (diff) | |
download | serenity-004025c3c4bfb3e9998e5c860700e357d70b41a4.zip |
LibSQL: Parse common-table-expressions with a nested SELECT statement
This also moves testing of common-table-expression to its own test case.
Diffstat (limited to 'Userland/Libraries/LibSQL')
-rw-r--r-- | Userland/Libraries/LibSQL/AST.h | 5 | ||||
-rw-r--r-- | Userland/Libraries/LibSQL/Parser.cpp | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibSQL/Tests/TestSqlStatementParser.cpp | 116 |
3 files changed, 71 insertions, 54 deletions
diff --git a/Userland/Libraries/LibSQL/AST.h b/Userland/Libraries/LibSQL/AST.h index fe8ff4d443..1c7a90eb26 100644 --- a/Userland/Libraries/LibSQL/AST.h +++ b/Userland/Libraries/LibSQL/AST.h @@ -83,18 +83,21 @@ private: class CommonTableExpression : public ASTNode { public: - CommonTableExpression(String table_name, Vector<String> column_names) + CommonTableExpression(String table_name, Vector<String> column_names, NonnullRefPtr<Select> select_statement) : m_table_name(move(table_name)) , m_column_names(move(column_names)) + , m_select_statement(move(select_statement)) { } const String& table_name() const { return m_table_name; } const Vector<String>& column_names() const { return m_column_names; } + const NonnullRefPtr<Select>& select_statement() const { return m_select_statement; } private: String m_table_name; Vector<String> m_column_names; + NonnullRefPtr<Select> m_select_statement; }; class CommonTableExpressionList : public ASTNode { diff --git a/Userland/Libraries/LibSQL/Parser.cpp b/Userland/Libraries/LibSQL/Parser.cpp index d1a7afda43..cd9e46afdb 100644 --- a/Userland/Libraries/LibSQL/Parser.cpp +++ b/Userland/Libraries/LibSQL/Parser.cpp @@ -719,10 +719,10 @@ NonnullRefPtr<CommonTableExpression> Parser::parse_common_table_expression() consume(TokenType::As); consume(TokenType::ParenOpen); - // FIXME: Parse "select-stmt". + auto select_statement = parse_select_statement({}); consume(TokenType::ParenClose); - return create_ast_node<CommonTableExpression>(move(table_name), move(column_names)); + return create_ast_node<CommonTableExpression>(move(table_name), move(column_names), move(select_statement)); } NonnullRefPtr<QualifiedTableName> Parser::parse_qualified_table_name() diff --git a/Userland/Libraries/LibSQL/Tests/TestSqlStatementParser.cpp b/Userland/Libraries/LibSQL/Tests/TestSqlStatementParser.cpp index 06b4b5f82d..946089a169 100644 --- a/Userland/Libraries/LibSQL/Tests/TestSqlStatementParser.cpp +++ b/Userland/Libraries/LibSQL/Tests/TestSqlStatementParser.cpp @@ -56,7 +56,6 @@ TEST_CASE(create_table) EXPECT(parse("CREATE TABLE test ( column1 varchar(.abc) )").is_error()); EXPECT(parse("CREATE TABLE test ( column1 varchar(0x) )").is_error()); EXPECT(parse("CREATE TABLE test ( column1 varchar(0xzzz) )").is_error()); - EXPECT(parse("WITH table AS () CREATE TABLE test ( column1 );").is_error()); EXPECT(parse("CREATE TABLE test ( column1 int ) AS SELECT * FROM table;").is_error()); EXPECT(parse("CREATE TABLE test AS SELECT * FROM table ( column1 int ) ;").is_error()); @@ -132,7 +131,6 @@ TEST_CASE(drop_table) EXPECT(parse("DROP TABLE").is_error()); EXPECT(parse("DROP TABLE test").is_error()); EXPECT(parse("DROP TABLE IF test;").is_error()); - EXPECT(parse("WITH table AS () DROP TABLE test;").is_error()); auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, bool expected_is_error_if_table_does_not_exist = true) { auto result = parse(sql); @@ -164,23 +162,8 @@ TEST_CASE(delete_) EXPECT(parse("DELETE FROM table WHERE 15 RETURNING column").is_error()); EXPECT(parse("DELETE FROM table WHERE 15 RETURNING column AS;").is_error()); EXPECT(parse("DELETE FROM table WHERE (');").is_error()); - EXPECT(parse("WITH DELETE FROM table;").is_error()); - EXPECT(parse("WITH table DELETE FROM table;").is_error()); - EXPECT(parse("WITH table AS DELETE FROM table;").is_error()); - EXPECT(parse("WITH RECURSIVE table DELETE FROM table;").is_error()); - EXPECT(parse("WITH RECURSIVE table AS DELETE FROM table;").is_error()); - - struct SelectedTableList { - struct SelectedTable { - StringView table_name {}; - Vector<StringView> column_names {}; - }; - - bool recursive { false }; - Vector<SelectedTable> selected_tables {}; - }; - auto validate = [](StringView sql, SelectedTableList expected_selected_tables, StringView expected_schema, StringView expected_table, StringView expected_alias, bool expect_where_clause, bool expect_returning_clause, Vector<StringView> expected_returned_column_aliases) { + auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, StringView expected_alias, bool expect_where_clause, bool expect_returning_clause, Vector<StringView> expected_returned_column_aliases) { auto result = parse(sql); EXPECT(!result.is_error()); @@ -189,25 +172,6 @@ TEST_CASE(delete_) const auto& delete_ = static_cast<const SQL::Delete&>(*statement); - const auto& common_table_expression_list = delete_.common_table_expression_list(); - EXPECT_EQ(common_table_expression_list.is_null(), expected_selected_tables.selected_tables.is_empty()); - if (common_table_expression_list) { - EXPECT_EQ(common_table_expression_list->recursive(), expected_selected_tables.recursive); - - const auto& common_table_expressions = common_table_expression_list->common_table_expressions(); - EXPECT_EQ(common_table_expressions.size(), expected_selected_tables.selected_tables.size()); - - for (size_t i = 0; i < common_table_expressions.size(); ++i) { - const auto& common_table_expression = common_table_expressions[i]; - const auto& expected_common_table_expression = expected_selected_tables.selected_tables[i]; - EXPECT_EQ(common_table_expression.table_name(), expected_common_table_expression.table_name); - EXPECT_EQ(common_table_expression.column_names().size(), expected_common_table_expression.column_names.size()); - - for (size_t j = 0; j < common_table_expression.column_names().size(); ++j) - EXPECT_EQ(common_table_expression.column_names()[j], expected_common_table_expression.column_names[j]); - } - } - const auto& qualified_table_name = delete_.qualified_table_name(); EXPECT_EQ(qualified_table_name->schema_name(), expected_schema); EXPECT_EQ(qualified_table_name->table_name(), expected_table); @@ -233,20 +197,14 @@ TEST_CASE(delete_) } }; - validate("DELETE FROM table;", {}, {}, "table", {}, false, false, {}); - validate("DELETE FROM schema.table;", {}, "schema", "table", {}, false, false, {}); - validate("DELETE FROM schema.table AS alias;", {}, "schema", "table", "alias", false, false, {}); - validate("DELETE FROM table WHERE (1 == 1);", {}, {}, "table", {}, true, false, {}); - validate("DELETE FROM table RETURNING *;", {}, {}, "table", {}, false, true, {}); - validate("DELETE FROM table RETURNING column;", {}, {}, "table", {}, false, true, { {} }); - validate("DELETE FROM table RETURNING column AS alias;", {}, {}, "table", {}, false, true, { "alias" }); - validate("DELETE FROM table RETURNING column1 AS alias1, column2 AS alias2;", {}, {}, "table", {}, false, true, { "alias1", "alias2" }); - - // FIXME: When parsing of SELECT statements are supported, the common-table-expressions below will become invalid due to the empty "AS ()" clause. - validate("WITH table AS () DELETE FROM table;", { false, { { "table" } } }, {}, "table", {}, false, false, {}); - validate("WITH table (column) AS () DELETE FROM table;", { false, { { "table", { "column" } } } }, {}, "table", {}, false, false, {}); - validate("WITH table (column1, column2) AS () DELETE FROM table;", { false, { { "table", { "column1", "column2" } } } }, {}, "table", {}, false, false, {}); - validate("WITH RECURSIVE table AS () DELETE FROM table;", { true, { { "table", {} } } }, {}, "table", {}, false, false, {}); + validate("DELETE FROM table;", {}, "table", {}, false, false, {}); + validate("DELETE FROM schema.table;", "schema", "table", {}, false, false, {}); + validate("DELETE FROM schema.table AS alias;", "schema", "table", "alias", false, false, {}); + validate("DELETE FROM table WHERE (1 == 1);", {}, "table", {}, true, false, {}); + validate("DELETE FROM table RETURNING *;", {}, "table", {}, false, true, {}); + validate("DELETE FROM table RETURNING column;", {}, "table", {}, false, true, { {} }); + validate("DELETE FROM table RETURNING column AS alias;", {}, "table", {}, false, true, { "alias" }); + validate("DELETE FROM table RETURNING column1 AS alias1, column2 AS alias2;", {}, "table", {}, false, true, { "alias1", "alias2" }); } TEST_CASE(select) @@ -415,4 +373,60 @@ TEST_CASE(select) validate("SELECT * FROM table LIMIT 15 OFFSET 16;", all, from, false, 0, false, {}, true, true); } +TEST_CASE(common_table_expression) +{ + EXPECT(parse("WITH DELETE FROM table;").is_error()); + EXPECT(parse("WITH table DELETE FROM table;").is_error()); + EXPECT(parse("WITH table AS DELETE FROM table;").is_error()); + EXPECT(parse("WITH RECURSIVE table DELETE FROM table;").is_error()); + EXPECT(parse("WITH RECURSIVE table AS DELETE FROM table;").is_error()); + + // Below are otherwise valid common-table-expressions, but attached to statements which do not allow them. + EXPECT(parse("WITH table AS (SELECT * AS TABLE) CREATE TABLE test ( column1 );").is_error()); + EXPECT(parse("WITH table AS (SELECT * FROM table) DROP TABLE test;").is_error()); + + struct SelectedTableList { + struct SelectedTable { + StringView table_name {}; + Vector<StringView> column_names {}; + }; + + bool recursive { false }; + Vector<SelectedTable> selected_tables {}; + }; + + auto validate = [](StringView sql, SelectedTableList expected_selected_tables) { + auto result = parse(sql); + EXPECT(!result.is_error()); + + auto statement = result.release_value(); + EXPECT(is<SQL::Delete>(*statement)); + + const auto& delete_ = static_cast<const SQL::Delete&>(*statement); + + const auto& common_table_expression_list = delete_.common_table_expression_list(); + EXPECT(!common_table_expression_list.is_null()); + + EXPECT_EQ(common_table_expression_list->recursive(), expected_selected_tables.recursive); + + const auto& common_table_expressions = common_table_expression_list->common_table_expressions(); + EXPECT_EQ(common_table_expressions.size(), expected_selected_tables.selected_tables.size()); + + for (size_t i = 0; i < common_table_expressions.size(); ++i) { + const auto& common_table_expression = common_table_expressions[i]; + const auto& expected_common_table_expression = expected_selected_tables.selected_tables[i]; + EXPECT_EQ(common_table_expression.table_name(), expected_common_table_expression.table_name); + EXPECT_EQ(common_table_expression.column_names().size(), expected_common_table_expression.column_names.size()); + + for (size_t j = 0; j < common_table_expression.column_names().size(); ++j) + EXPECT_EQ(common_table_expression.column_names()[j], expected_common_table_expression.column_names[j]); + } + }; + + validate("WITH table AS (SELECT * FROM table) DELETE FROM table;", { false, { { "table" } } }); + validate("WITH table (column) AS (SELECT * FROM table) DELETE FROM table;", { false, { { "table", { "column" } } } }); + validate("WITH table (column1, column2) AS (SELECT * FROM table) DELETE FROM table;", { false, { { "table", { "column1", "column2" } } } }); + validate("WITH RECURSIVE table AS (SELECT * FROM table) DELETE FROM table;", { true, { { "table", {} } } }); +} + TEST_MAIN(SqlStatementParser) |