mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 05:08:13 +00:00
LibSQL: Parse INSERT statement
This also adds missing '&' on a couple AST getter methods.
This commit is contained in:
parent
35f0450dd8
commit
8d79b4a3e1
5 changed files with 218 additions and 4 deletions
|
@ -58,7 +58,7 @@ public:
|
|||
}
|
||||
|
||||
const String& name() const { return m_name; }
|
||||
const NonnullRefPtrVector<SignedNumber> signed_numbers() const { return m_signed_numbers; }
|
||||
const NonnullRefPtrVector<SignedNumber>& signed_numbers() const { return m_signed_numbers; }
|
||||
|
||||
private:
|
||||
String m_name;
|
||||
|
@ -706,7 +706,7 @@ public:
|
|||
const RefPtr<Select>& select_statement() const { return m_select_statement; }
|
||||
|
||||
bool has_columns() const { return !m_columns.is_empty(); }
|
||||
const NonnullRefPtrVector<ColumnDefinition> columns() const { return m_columns; }
|
||||
const NonnullRefPtrVector<ColumnDefinition>& columns() const { return m_columns; }
|
||||
|
||||
bool is_temporary() const { return m_is_temporary; }
|
||||
bool is_error_if_table_exists() const { return m_is_error_if_table_exists; }
|
||||
|
@ -739,6 +739,74 @@ private:
|
|||
bool m_is_error_if_table_does_not_exist;
|
||||
};
|
||||
|
||||
enum class ConflictResolution {
|
||||
Abort,
|
||||
Fail,
|
||||
Ignore,
|
||||
Replace,
|
||||
Rollback,
|
||||
};
|
||||
|
||||
class Insert : public Statement {
|
||||
public:
|
||||
Insert(RefPtr<CommonTableExpressionList> common_table_expression_list, ConflictResolution conflict_resolution, String schema_name, String table_name, String alias, Vector<String> column_names, NonnullRefPtrVector<ChainedExpression> chained_expressions)
|
||||
: m_common_table_expression_list(move(common_table_expression_list))
|
||||
, m_conflict_resolution(conflict_resolution)
|
||||
, m_schema_name(move(schema_name))
|
||||
, m_table_name(move(table_name))
|
||||
, m_alias(move(alias))
|
||||
, m_column_names(move(column_names))
|
||||
, m_chained_expressions(move(chained_expressions))
|
||||
{
|
||||
}
|
||||
|
||||
Insert(RefPtr<CommonTableExpressionList> common_table_expression_list, ConflictResolution conflict_resolution, String schema_name, String table_name, String alias, Vector<String> column_names, RefPtr<Select> select_statement)
|
||||
: m_common_table_expression_list(move(common_table_expression_list))
|
||||
, m_conflict_resolution(conflict_resolution)
|
||||
, m_schema_name(move(schema_name))
|
||||
, m_table_name(move(table_name))
|
||||
, m_alias(move(alias))
|
||||
, m_column_names(move(column_names))
|
||||
, m_select_statement(move(select_statement))
|
||||
{
|
||||
}
|
||||
|
||||
Insert(RefPtr<CommonTableExpressionList> common_table_expression_list, ConflictResolution conflict_resolution, String schema_name, String table_name, String alias, Vector<String> column_names)
|
||||
: m_common_table_expression_list(move(common_table_expression_list))
|
||||
, m_conflict_resolution(conflict_resolution)
|
||||
, m_schema_name(move(schema_name))
|
||||
, m_table_name(move(table_name))
|
||||
, m_alias(move(alias))
|
||||
, m_column_names(move(column_names))
|
||||
{
|
||||
}
|
||||
|
||||
const RefPtr<CommonTableExpressionList>& common_table_expression_list() const { return m_common_table_expression_list; }
|
||||
ConflictResolution conflict_resolution() const { return m_conflict_resolution; }
|
||||
const String& schema_name() const { return m_schema_name; }
|
||||
const String& table_name() const { return m_table_name; }
|
||||
const String& alias() const { return m_alias; }
|
||||
const Vector<String>& column_names() const { return m_column_names; }
|
||||
|
||||
bool default_values() const { return !has_expressions() && !has_selection(); };
|
||||
|
||||
bool has_expressions() const { return !m_chained_expressions.is_empty(); }
|
||||
const NonnullRefPtrVector<ChainedExpression>& chained_expressions() const { return m_chained_expressions; }
|
||||
|
||||
bool has_selection() const { return !m_select_statement.is_null(); }
|
||||
const RefPtr<Select>& select_statement() const { return m_select_statement; }
|
||||
|
||||
private:
|
||||
RefPtr<CommonTableExpressionList> m_common_table_expression_list;
|
||||
ConflictResolution m_conflict_resolution;
|
||||
String m_schema_name;
|
||||
String m_table_name;
|
||||
String m_alias;
|
||||
Vector<String> m_column_names;
|
||||
NonnullRefPtrVector<ChainedExpression> m_chained_expressions;
|
||||
RefPtr<Select> m_select_statement;
|
||||
};
|
||||
|
||||
class Delete : public Statement {
|
||||
public:
|
||||
Delete(RefPtr<CommonTableExpressionList> common_table_expression_list, NonnullRefPtr<QualifiedTableName> qualified_table_name, RefPtr<Expression> where_clause, RefPtr<ReturningClause> returning_clause)
|
||||
|
|
|
@ -29,6 +29,7 @@ class Expression;
|
|||
class GroupByClause;
|
||||
class InChainedExpression;
|
||||
class InSelectionExpression;
|
||||
class Insert;
|
||||
class InTableExpression;
|
||||
class InvertibleNestedDoubleExpression;
|
||||
class InvertibleNestedExpression;
|
||||
|
|
|
@ -36,12 +36,14 @@ NonnullRefPtr<Statement> Parser::parse_statement()
|
|||
return parse_create_table_statement();
|
||||
case TokenType::Drop:
|
||||
return parse_drop_table_statement();
|
||||
case TokenType::Insert:
|
||||
return parse_insert_statement({});
|
||||
case TokenType::Delete:
|
||||
return parse_delete_statement({});
|
||||
case TokenType::Select:
|
||||
return parse_select_statement({});
|
||||
default:
|
||||
expected("CREATE, DROP, DELETE, or SELECT");
|
||||
expected("CREATE, DROP, INSERT, DELETE, or SELECT");
|
||||
return create_ast_node<ErrorStatement>();
|
||||
}
|
||||
}
|
||||
|
@ -49,12 +51,14 @@ NonnullRefPtr<Statement> Parser::parse_statement()
|
|||
NonnullRefPtr<Statement> Parser::parse_statement_with_expression_list(RefPtr<CommonTableExpressionList> common_table_expression_list)
|
||||
{
|
||||
switch (m_parser_state.m_token.type()) {
|
||||
case TokenType::Insert:
|
||||
return parse_insert_statement(move(common_table_expression_list));
|
||||
case TokenType::Delete:
|
||||
return parse_delete_statement(move(common_table_expression_list));
|
||||
case TokenType::Select:
|
||||
return parse_select_statement(move(common_table_expression_list));
|
||||
default:
|
||||
expected("DELETE or SELECT");
|
||||
expected("INSERT, DELETE or SELECT");
|
||||
return create_ast_node<ErrorStatement>();
|
||||
}
|
||||
}
|
||||
|
@ -113,6 +117,73 @@ NonnullRefPtr<DropTable> Parser::parse_drop_table_statement()
|
|||
return create_ast_node<DropTable>(move(schema_name), move(table_name), is_error_if_table_does_not_exist);
|
||||
}
|
||||
|
||||
NonnullRefPtr<Delete> Parser::parse_insert_statement(RefPtr<CommonTableExpressionList> common_table_expression_list)
|
||||
{
|
||||
// https://sqlite.org/lang_insert.html
|
||||
consume(TokenType::Insert);
|
||||
|
||||
auto conflict_resolution = ConflictResolution::Abort;
|
||||
if (consume_if(TokenType::Or)) {
|
||||
// https://sqlite.org/lang_conflict.html
|
||||
if (consume_if(TokenType::Abort))
|
||||
conflict_resolution = ConflictResolution::Abort;
|
||||
else if (consume_if(TokenType::Fail))
|
||||
conflict_resolution = ConflictResolution::Fail;
|
||||
else if (consume_if(TokenType::Ignore))
|
||||
conflict_resolution = ConflictResolution::Ignore;
|
||||
else if (consume_if(TokenType::Replace))
|
||||
conflict_resolution = ConflictResolution::Replace;
|
||||
else if (consume_if(TokenType::Rollback))
|
||||
conflict_resolution = ConflictResolution::Rollback;
|
||||
else
|
||||
expected("ABORT, FAIL, IGNORE, REPLACE, or ROLLBACK");
|
||||
}
|
||||
|
||||
consume(TokenType::Into);
|
||||
|
||||
String schema_name;
|
||||
String table_name;
|
||||
parse_schema_and_table_name(schema_name, table_name);
|
||||
|
||||
String alias;
|
||||
if (consume_if(TokenType::As))
|
||||
alias = consume(TokenType::Identifier).value();
|
||||
|
||||
Vector<String> column_names;
|
||||
if (match(TokenType::ParenOpen))
|
||||
parse_comma_separated_list(true, [&]() { column_names.append(consume(TokenType::Identifier).value()); });
|
||||
|
||||
NonnullRefPtrVector<ChainedExpression> chained_expressions;
|
||||
RefPtr<Select> select_statement;
|
||||
|
||||
if (consume_if(TokenType::Values)) {
|
||||
parse_comma_separated_list(false, [&]() {
|
||||
if (auto chained_expression = parse_chained_expression(); chained_expression.has_value())
|
||||
chained_expressions.append(move(chained_expression.value()));
|
||||
else
|
||||
expected("Chained expression");
|
||||
});
|
||||
} else if (match(TokenType::Select)) {
|
||||
select_statement = parse_select_statement({});
|
||||
} else {
|
||||
consume(TokenType::Default);
|
||||
consume(TokenType::Values);
|
||||
}
|
||||
|
||||
RefPtr<ReturningClause> returning_clause;
|
||||
if (match(TokenType::Returning))
|
||||
returning_clause = parse_returning_clause();
|
||||
|
||||
// FIXME: Parse 'upsert-clause'.
|
||||
|
||||
if (!chained_expressions.is_empty())
|
||||
return create_ast_node<Insert>(move(common_table_expression_list), conflict_resolution, move(schema_name), move(table_name), move(alias), move(column_names), move(chained_expressions));
|
||||
if (!select_statement.is_null())
|
||||
return create_ast_node<Insert>(move(common_table_expression_list), conflict_resolution, move(schema_name), move(table_name), move(alias), move(column_names), move(select_statement));
|
||||
|
||||
return create_ast_node<Insert>(move(common_table_expression_list), conflict_resolution, move(schema_name), move(table_name), move(alias), move(column_names));
|
||||
}
|
||||
|
||||
NonnullRefPtr<Delete> Parser::parse_delete_statement(RefPtr<CommonTableExpressionList> common_table_expression_list)
|
||||
{
|
||||
// https://sqlite.org/lang_delete.html
|
||||
|
|
|
@ -54,6 +54,7 @@ private:
|
|||
NonnullRefPtr<Statement> parse_statement_with_expression_list(RefPtr<CommonTableExpressionList>);
|
||||
NonnullRefPtr<CreateTable> parse_create_table_statement();
|
||||
NonnullRefPtr<DropTable> parse_drop_table_statement();
|
||||
NonnullRefPtr<Delete> parse_insert_statement(RefPtr<CommonTableExpressionList>);
|
||||
NonnullRefPtr<Delete> parse_delete_statement(RefPtr<CommonTableExpressionList>);
|
||||
NonnullRefPtr<Select> parse_select_statement(RefPtr<CommonTableExpressionList>);
|
||||
NonnullRefPtr<CommonTableExpressionList> parse_common_table_expression_list();
|
||||
|
|
|
@ -150,6 +150,79 @@ TEST_CASE(drop_table)
|
|||
validate("DROP TABLE IF EXISTS test;", {}, "test", false);
|
||||
}
|
||||
|
||||
TEST_CASE(insert)
|
||||
{
|
||||
EXPECT(parse("INSERT").is_error());
|
||||
EXPECT(parse("INSERT INTO").is_error());
|
||||
EXPECT(parse("INSERT INTO table").is_error());
|
||||
EXPECT(parse("INSERT INTO table (column)").is_error());
|
||||
EXPECT(parse("INSERT INTO table (column, ) DEFAULT VALUES;").is_error());
|
||||
EXPECT(parse("INSERT INTO table VALUES").is_error());
|
||||
EXPECT(parse("INSERT INTO table VALUES ();").is_error());
|
||||
EXPECT(parse("INSERT INTO table VALUES (1)").is_error());
|
||||
EXPECT(parse("INSERT INTO table SELECT").is_error());
|
||||
EXPECT(parse("INSERT INTO table SELECT * from table").is_error());
|
||||
EXPECT(parse("INSERT OR INTO table DEFAULT VALUES;").is_error());
|
||||
EXPECT(parse("INSERT OR foo INTO table DEFAULT VALUES;").is_error());
|
||||
|
||||
auto validate = [](StringView sql, SQL::ConflictResolution expected_conflict_resolution, StringView expected_schema, StringView expected_table, StringView expected_alias, Vector<StringView> expected_column_names, Vector<size_t> expected_chain_sizes, bool expect_select_statement) {
|
||||
auto result = parse(sql);
|
||||
EXPECT(!result.is_error());
|
||||
|
||||
auto statement = result.release_value();
|
||||
EXPECT(is<SQL::Insert>(*statement));
|
||||
|
||||
const auto& insert = static_cast<const SQL::Insert&>(*statement);
|
||||
EXPECT_EQ(insert.conflict_resolution(), expected_conflict_resolution);
|
||||
EXPECT_EQ(insert.schema_name(), expected_schema);
|
||||
EXPECT_EQ(insert.table_name(), expected_table);
|
||||
EXPECT_EQ(insert.alias(), expected_alias);
|
||||
|
||||
const auto& column_names = insert.column_names();
|
||||
EXPECT_EQ(column_names.size(), expected_column_names.size());
|
||||
for (size_t i = 0; i < column_names.size(); ++i)
|
||||
EXPECT_EQ(column_names[i], expected_column_names[i]);
|
||||
|
||||
EXPECT_EQ(insert.has_expressions(), !expected_chain_sizes.is_empty());
|
||||
if (insert.has_expressions()) {
|
||||
const auto& chained_expressions = insert.chained_expressions();
|
||||
EXPECT_EQ(chained_expressions.size(), expected_chain_sizes.size());
|
||||
|
||||
for (size_t i = 0; i < chained_expressions.size(); ++i) {
|
||||
const auto& chained_expression = chained_expressions[i];
|
||||
const auto& expressions = chained_expression.expressions();
|
||||
EXPECT_EQ(expressions.size(), expected_chain_sizes[i]);
|
||||
|
||||
for (const auto& expression : expressions)
|
||||
EXPECT(!is<SQL::ErrorExpression>(expression));
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(insert.has_selection(), expect_select_statement);
|
||||
EXPECT_EQ(insert.default_values(), expected_chain_sizes.is_empty() && !expect_select_statement);
|
||||
};
|
||||
|
||||
validate("INSERT OR ABORT INTO table DEFAULT VALUES;", SQL::ConflictResolution::Abort, {}, "table", {}, {}, {}, false);
|
||||
validate("INSERT OR FAIL INTO table DEFAULT VALUES;", SQL::ConflictResolution::Fail, {}, "table", {}, {}, {}, false);
|
||||
validate("INSERT OR IGNORE INTO table DEFAULT VALUES;", SQL::ConflictResolution::Ignore, {}, "table", {}, {}, {}, false);
|
||||
validate("INSERT OR REPLACE INTO table DEFAULT VALUES;", SQL::ConflictResolution::Replace, {}, "table", {}, {}, {}, false);
|
||||
validate("INSERT OR ROLLBACK INTO table DEFAULT VALUES;", SQL::ConflictResolution::Rollback, {}, "table", {}, {}, {}, false);
|
||||
|
||||
auto resolution = SQL::ConflictResolution::Abort;
|
||||
validate("INSERT INTO table DEFAULT VALUES;", resolution, {}, "table", {}, {}, {}, false);
|
||||
validate("INSERT INTO schema.table DEFAULT VALUES;", resolution, "schema", "table", {}, {}, {}, false);
|
||||
validate("INSERT INTO table AS foo DEFAULT VALUES;", resolution, {}, "table", "foo", {}, {}, false);
|
||||
|
||||
validate("INSERT INTO table (column) DEFAULT VALUES;", resolution, {}, "table", {}, { "column" }, {}, false);
|
||||
validate("INSERT INTO table (column1, column2) DEFAULT VALUES;", resolution, {}, "table", {}, { "column1", "column2" }, {}, false);
|
||||
|
||||
validate("INSERT INTO table VALUES (1);", resolution, {}, "table", {}, {}, { 1 }, false);
|
||||
validate("INSERT INTO table VALUES (1, 2);", resolution, {}, "table", {}, {}, { 2 }, false);
|
||||
validate("INSERT INTO table VALUES (1, 2), (3, 4, 5);", resolution, {}, "table", {}, {}, { 2, 3 }, false);
|
||||
|
||||
validate("INSERT INTO table SELECT * FROM table;", resolution, {}, "table", {}, {}, {}, true);
|
||||
}
|
||||
|
||||
TEST_CASE(delete_)
|
||||
{
|
||||
EXPECT(parse("DELETE").is_error());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue