mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 22:07:34 +00:00
LibSQL: Parse ALTER TABLE statement
There are 4 forms an ALTER TABLE statement can take, and each are very distinct, so they each get their own AST node class.
This commit is contained in:
parent
0764a68616
commit
1500479a1d
5 changed files with 262 additions and 1 deletions
|
@ -720,6 +720,82 @@ private:
|
||||||
bool m_is_error_if_table_exists;
|
bool m_is_error_if_table_exists;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AlterTable : public Statement {
|
||||||
|
public:
|
||||||
|
const String& schema_name() const { return m_schema_name; }
|
||||||
|
const String& table_name() const { return m_table_name; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AlterTable(String schema_name, String table_name)
|
||||||
|
: m_schema_name(move(schema_name))
|
||||||
|
, m_table_name(move(table_name))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
String m_schema_name;
|
||||||
|
String m_table_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RenameTable : public AlterTable {
|
||||||
|
public:
|
||||||
|
RenameTable(String schema_name, String table_name, String new_table_name)
|
||||||
|
: AlterTable(move(schema_name), move(table_name))
|
||||||
|
, m_new_table_name(move(new_table_name))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const String& new_table_name() const { return m_new_table_name; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
String m_new_table_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RenameColumn : public AlterTable {
|
||||||
|
public:
|
||||||
|
RenameColumn(String schema_name, String table_name, String column_name, String new_column_name)
|
||||||
|
: AlterTable(move(schema_name), move(table_name))
|
||||||
|
, m_column_name(move(column_name))
|
||||||
|
, m_new_column_name(move(new_column_name))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const String& column_name() const { return m_column_name; }
|
||||||
|
const String& new_column_name() const { return m_new_column_name; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
String m_column_name;
|
||||||
|
String m_new_column_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AddColumn : public AlterTable {
|
||||||
|
public:
|
||||||
|
AddColumn(String schema_name, String table_name, NonnullRefPtr<ColumnDefinition> column)
|
||||||
|
: AlterTable(move(schema_name), move(table_name))
|
||||||
|
, m_column(move(column))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonnullRefPtr<ColumnDefinition>& column() const { return m_column; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
NonnullRefPtr<ColumnDefinition> m_column;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DropColumn : public AlterTable {
|
||||||
|
public:
|
||||||
|
DropColumn(String schema_name, String table_name, String column_name)
|
||||||
|
: AlterTable(move(schema_name), move(table_name))
|
||||||
|
, m_column_name(move(column_name))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const String& column_name() const { return m_column_name; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
String m_column_name;
|
||||||
|
};
|
||||||
|
|
||||||
class DropTable : public Statement {
|
class DropTable : public Statement {
|
||||||
public:
|
public:
|
||||||
DropTable(String schema_name, String table_name, bool is_error_if_table_does_not_exist)
|
DropTable(String schema_name, String table_name, bool is_error_if_table_does_not_exist)
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace SQL {
|
namespace SQL {
|
||||||
|
class AddColumn;
|
||||||
|
class AlterTable;
|
||||||
class ASTNode;
|
class ASTNode;
|
||||||
class BetweenExpression;
|
class BetweenExpression;
|
||||||
class BinaryOperatorExpression;
|
class BinaryOperatorExpression;
|
||||||
|
@ -21,6 +23,7 @@ class CommonTableExpression;
|
||||||
class CommonTableExpressionList;
|
class CommonTableExpressionList;
|
||||||
class CreateTable;
|
class CreateTable;
|
||||||
class Delete;
|
class Delete;
|
||||||
|
class DropColumn;
|
||||||
class DropTable;
|
class DropTable;
|
||||||
class ErrorExpression;
|
class ErrorExpression;
|
||||||
class ErrorStatement;
|
class ErrorStatement;
|
||||||
|
@ -45,6 +48,8 @@ class NumericLiteral;
|
||||||
class OrderingTerm;
|
class OrderingTerm;
|
||||||
class Parser;
|
class Parser;
|
||||||
class QualifiedTableName;
|
class QualifiedTableName;
|
||||||
|
class RenameColumn;
|
||||||
|
class RenameTable;
|
||||||
class ResultColumn;
|
class ResultColumn;
|
||||||
class ReturningClause;
|
class ReturningClause;
|
||||||
class Select;
|
class Select;
|
||||||
|
|
|
@ -34,6 +34,8 @@ NonnullRefPtr<Statement> Parser::parse_statement()
|
||||||
switch (m_parser_state.m_token.type()) {
|
switch (m_parser_state.m_token.type()) {
|
||||||
case TokenType::Create:
|
case TokenType::Create:
|
||||||
return parse_create_table_statement();
|
return parse_create_table_statement();
|
||||||
|
case TokenType::Alter:
|
||||||
|
return parse_alter_table_statement();
|
||||||
case TokenType::Drop:
|
case TokenType::Drop:
|
||||||
return parse_drop_table_statement();
|
return parse_drop_table_statement();
|
||||||
case TokenType::Insert:
|
case TokenType::Insert:
|
||||||
|
@ -45,7 +47,7 @@ NonnullRefPtr<Statement> Parser::parse_statement()
|
||||||
case TokenType::Select:
|
case TokenType::Select:
|
||||||
return parse_select_statement({});
|
return parse_select_statement({});
|
||||||
default:
|
default:
|
||||||
expected("CREATE, DROP, INSERT, UPDATE, DELETE, or SELECT");
|
expected("CREATE, ALTER, DROP, INSERT, UPDATE, DELETE, or SELECT");
|
||||||
return create_ast_node<ErrorStatement>();
|
return create_ast_node<ErrorStatement>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,6 +104,42 @@ NonnullRefPtr<CreateTable> Parser::parse_create_table_statement()
|
||||||
return create_ast_node<CreateTable>(move(schema_name), move(table_name), move(column_definitions), is_temporary, is_error_if_table_exists);
|
return create_ast_node<CreateTable>(move(schema_name), move(table_name), move(column_definitions), is_temporary, is_error_if_table_exists);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NonnullRefPtr<CreateTable> Parser::parse_alter_table_statement()
|
||||||
|
{
|
||||||
|
// https://sqlite.org/lang_altertable.html
|
||||||
|
consume(TokenType::Alter);
|
||||||
|
consume(TokenType::Table);
|
||||||
|
|
||||||
|
String schema_name;
|
||||||
|
String table_name;
|
||||||
|
parse_schema_and_table_name(schema_name, table_name);
|
||||||
|
|
||||||
|
if (consume_if(TokenType::Add)) {
|
||||||
|
consume_if(TokenType::Column); // COLUMN is optional.
|
||||||
|
auto column = parse_column_definition();
|
||||||
|
return create_ast_node<AddColumn>(move(schema_name), move(table_name), move(column));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (consume_if(TokenType::Drop)) {
|
||||||
|
consume_if(TokenType::Column); // COLUMN is optional.
|
||||||
|
auto column = consume(TokenType::Identifier).value();
|
||||||
|
return create_ast_node<DropColumn>(move(schema_name), move(table_name), move(column));
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(TokenType::Rename);
|
||||||
|
|
||||||
|
if (consume_if(TokenType::To)) {
|
||||||
|
auto new_table_name = consume(TokenType::Identifier).value();
|
||||||
|
return create_ast_node<RenameTable>(move(schema_name), move(table_name), move(new_table_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
consume_if(TokenType::Column); // COLUMN is optional.
|
||||||
|
auto column_name = consume(TokenType::Identifier).value();
|
||||||
|
consume(TokenType::To);
|
||||||
|
auto new_column_name = consume(TokenType::Identifier).value();
|
||||||
|
return create_ast_node<RenameColumn>(move(schema_name), move(table_name), move(column_name), move(new_column_name));
|
||||||
|
}
|
||||||
|
|
||||||
NonnullRefPtr<DropTable> Parser::parse_drop_table_statement()
|
NonnullRefPtr<DropTable> Parser::parse_drop_table_statement()
|
||||||
{
|
{
|
||||||
// https://sqlite.org/lang_droptable.html
|
// https://sqlite.org/lang_droptable.html
|
||||||
|
|
|
@ -53,6 +53,7 @@ private:
|
||||||
NonnullRefPtr<Statement> parse_statement();
|
NonnullRefPtr<Statement> parse_statement();
|
||||||
NonnullRefPtr<Statement> parse_statement_with_expression_list(RefPtr<CommonTableExpressionList>);
|
NonnullRefPtr<Statement> parse_statement_with_expression_list(RefPtr<CommonTableExpressionList>);
|
||||||
NonnullRefPtr<CreateTable> parse_create_table_statement();
|
NonnullRefPtr<CreateTable> parse_create_table_statement();
|
||||||
|
NonnullRefPtr<CreateTable> parse_alter_table_statement();
|
||||||
NonnullRefPtr<DropTable> parse_drop_table_statement();
|
NonnullRefPtr<DropTable> parse_drop_table_statement();
|
||||||
NonnullRefPtr<Delete> parse_insert_statement(RefPtr<CommonTableExpressionList>);
|
NonnullRefPtr<Delete> parse_insert_statement(RefPtr<CommonTableExpressionList>);
|
||||||
NonnullRefPtr<Delete> parse_update_statement(RefPtr<CommonTableExpressionList>);
|
NonnullRefPtr<Delete> parse_update_statement(RefPtr<CommonTableExpressionList>);
|
||||||
|
|
|
@ -125,6 +125,147 @@ TEST_CASE(create_table)
|
||||||
validate("CREATE TABLE test ( column1 varchar(1e3) );", {}, "test", { { "column1", "varchar", { 1000 } } });
|
validate("CREATE TABLE test ( column1 varchar(1e3) );", {}, "test", { { "column1", "varchar", { 1000 } } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE(alter_table)
|
||||||
|
{
|
||||||
|
// This test case only contains common error cases of the AlterTable subclasses.
|
||||||
|
EXPECT(parse("ALTER").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table;").is_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(alter_table_rename_table)
|
||||||
|
{
|
||||||
|
EXPECT(parse("ALTER TABLE table RENAME").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table RENAME TO").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table RENAME TO new_table").is_error());
|
||||||
|
|
||||||
|
auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, StringView expected_new_table) {
|
||||||
|
auto result = parse(sql);
|
||||||
|
EXPECT(!result.is_error());
|
||||||
|
|
||||||
|
auto statement = result.release_value();
|
||||||
|
EXPECT(is<SQL::RenameTable>(*statement));
|
||||||
|
|
||||||
|
const auto& alter = static_cast<const SQL::RenameTable&>(*statement);
|
||||||
|
EXPECT_EQ(alter.schema_name(), expected_schema);
|
||||||
|
EXPECT_EQ(alter.table_name(), expected_table);
|
||||||
|
EXPECT_EQ(alter.new_table_name(), expected_new_table);
|
||||||
|
};
|
||||||
|
|
||||||
|
validate("ALTER TABLE table RENAME TO new_table;", {}, "table", "new_table");
|
||||||
|
validate("ALTER TABLE schema.table RENAME TO new_table;", "schema", "table", "new_table");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(alter_table_rename_column)
|
||||||
|
{
|
||||||
|
EXPECT(parse("ALTER TABLE table RENAME").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table RENAME COLUMN").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table RENAME COLUMN column").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table RENAME COLUMN column TO").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table RENAME COLUMN column TO new_column").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table RENAME column").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table RENAME column TO").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table RENAME column TO new_column").is_error());
|
||||||
|
|
||||||
|
auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, StringView expected_column, StringView expected_new_column) {
|
||||||
|
auto result = parse(sql);
|
||||||
|
EXPECT(!result.is_error());
|
||||||
|
|
||||||
|
auto statement = result.release_value();
|
||||||
|
EXPECT(is<SQL::RenameColumn>(*statement));
|
||||||
|
|
||||||
|
const auto& alter = static_cast<const SQL::RenameColumn&>(*statement);
|
||||||
|
EXPECT_EQ(alter.schema_name(), expected_schema);
|
||||||
|
EXPECT_EQ(alter.table_name(), expected_table);
|
||||||
|
EXPECT_EQ(alter.column_name(), expected_column);
|
||||||
|
EXPECT_EQ(alter.new_column_name(), expected_new_column);
|
||||||
|
};
|
||||||
|
|
||||||
|
validate("ALTER TABLE table RENAME column TO new_column;", {}, "table", "column", "new_column");
|
||||||
|
validate("ALTER TABLE table RENAME COLUMN column TO new_column;", {}, "table", "column", "new_column");
|
||||||
|
validate("ALTER TABLE schema.table RENAME column TO new_column;", "schema", "table", "column", "new_column");
|
||||||
|
validate("ALTER TABLE schema.table RENAME COLUMN column TO new_column;", "schema", "table", "column", "new_column");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(alter_table_add_column)
|
||||||
|
{
|
||||||
|
EXPECT(parse("ALTER TABLE table ADD").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table ADD COLUMN").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table ADD COLUMN column").is_error());
|
||||||
|
|
||||||
|
struct Column {
|
||||||
|
StringView name;
|
||||||
|
StringView type;
|
||||||
|
Vector<double> signed_numbers {};
|
||||||
|
};
|
||||||
|
|
||||||
|
auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, Column expected_column) {
|
||||||
|
auto result = parse(sql);
|
||||||
|
EXPECT(!result.is_error());
|
||||||
|
|
||||||
|
auto statement = result.release_value();
|
||||||
|
EXPECT(is<SQL::AddColumn>(*statement));
|
||||||
|
|
||||||
|
const auto& alter = static_cast<const SQL::AddColumn&>(*statement);
|
||||||
|
EXPECT_EQ(alter.schema_name(), expected_schema);
|
||||||
|
EXPECT_EQ(alter.table_name(), expected_table);
|
||||||
|
|
||||||
|
const auto& column = alter.column();
|
||||||
|
EXPECT_EQ(column->name(), expected_column.name);
|
||||||
|
|
||||||
|
const auto& type_name = column->type_name();
|
||||||
|
EXPECT_EQ(type_name->name(), expected_column.type);
|
||||||
|
|
||||||
|
const auto& signed_numbers = type_name->signed_numbers();
|
||||||
|
EXPECT_EQ(signed_numbers.size(), expected_column.signed_numbers.size());
|
||||||
|
|
||||||
|
for (size_t j = 0; j < signed_numbers.size(); ++j) {
|
||||||
|
double signed_number = signed_numbers[j].value();
|
||||||
|
double expected_signed_number = expected_column.signed_numbers[j];
|
||||||
|
EXPECT_EQ(signed_number, expected_signed_number);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
validate("ALTER TABLE test ADD column1;", {}, "test", { "column1", "BLOB" });
|
||||||
|
validate("ALTER TABLE test ADD column1 int;", {}, "test", { "column1", "int" });
|
||||||
|
validate("ALTER TABLE test ADD column1 varchar;", {}, "test", { "column1", "varchar" });
|
||||||
|
validate("ALTER TABLE test ADD column1 varchar(255);", {}, "test", { "column1", "varchar", { 255 } });
|
||||||
|
validate("ALTER TABLE test ADD column1 varchar(255, 123);", {}, "test", { "column1", "varchar", { 255, 123 } });
|
||||||
|
|
||||||
|
validate("ALTER TABLE schema.test ADD COLUMN column1;", "schema", "test", { "column1", "BLOB" });
|
||||||
|
validate("ALTER TABLE schema.test ADD COLUMN column1 int;", "schema", "test", { "column1", "int" });
|
||||||
|
validate("ALTER TABLE schema.test ADD COLUMN column1 varchar;", "schema", "test", { "column1", "varchar" });
|
||||||
|
validate("ALTER TABLE schema.test ADD COLUMN column1 varchar(255);", "schema", "test", { "column1", "varchar", { 255 } });
|
||||||
|
validate("ALTER TABLE schema.test ADD COLUMN column1 varchar(255, 123);", "schema", "test", { "column1", "varchar", { 255, 123 } });
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(alter_table_drop_column)
|
||||||
|
{
|
||||||
|
EXPECT(parse("ALTER TABLE table DROP").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table DROP COLUMN").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table DROP column").is_error());
|
||||||
|
EXPECT(parse("ALTER TABLE table DROP COLUMN column").is_error());
|
||||||
|
|
||||||
|
auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, StringView expected_column) {
|
||||||
|
auto result = parse(sql);
|
||||||
|
EXPECT(!result.is_error());
|
||||||
|
|
||||||
|
auto statement = result.release_value();
|
||||||
|
EXPECT(is<SQL::DropColumn>(*statement));
|
||||||
|
|
||||||
|
const auto& alter = static_cast<const SQL::DropColumn&>(*statement);
|
||||||
|
EXPECT_EQ(alter.schema_name(), expected_schema);
|
||||||
|
EXPECT_EQ(alter.table_name(), expected_table);
|
||||||
|
EXPECT_EQ(alter.column_name(), expected_column);
|
||||||
|
};
|
||||||
|
|
||||||
|
validate("ALTER TABLE table DROP column;", {}, "table", "column");
|
||||||
|
validate("ALTER TABLE table DROP COLUMN column;", {}, "table", "column");
|
||||||
|
validate("ALTER TABLE schema.table DROP column;", "schema", "table", "column");
|
||||||
|
validate("ALTER TABLE schema.table DROP COLUMN column;", "schema", "table", "column");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE(drop_table)
|
TEST_CASE(drop_table)
|
||||||
{
|
{
|
||||||
EXPECT(parse("DROP").is_error());
|
EXPECT(parse("DROP").is_error());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue