1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 12:47:45 +00:00

LibSQL: Invent statement execution machinery and CREATE SCHEMA statement

This patch introduces the ability execute parsed SQL statements. The
abstract AST Statement node now has a virtual 'execute' method. This
method takes a Database object as parameter and returns a SQLResult
object.

Also introduced here is the CREATE SCHEMA statement. Tables live in a
schema, and if no schema is present in a table reference the 'default'
schema is implied. This schema is created if it doesn't yet exist when
a Database object is created.

Finally, as a proof of concept, the CREATE SCHEMA and CREATE TABLE
statements received an 'execute' implementation. The CREATE TABLE
method is not able to create tables created from SQL queries yet.
This commit is contained in:
Jan de Visser 2021-06-27 21:32:22 -04:00 committed by Ali Mohammad Pur
parent 30691549fd
commit 1037d6b0eb
9 changed files with 147 additions and 33 deletions

View file

@ -666,11 +666,31 @@ private:
//==================================================================================================
class Statement : public ASTNode {
public:
virtual RefPtr<SQLResult> execute(NonnullRefPtr<Database>) const { return nullptr; }
};
class ErrorStatement final : public Statement {
};
class CreateSchema : public Statement {
public:
CreateSchema(String schema_name, bool is_error_if_schema_exists)
: m_schema_name(move(schema_name))
, m_is_error_if_schema_exists(is_error_if_schema_exists)
{
}
const String& schema_name() const { return m_schema_name; }
bool is_error_if_schema_exists() const { return m_is_error_if_schema_exists; }
RefPtr<SQLResult> execute(NonnullRefPtr<Database>) const override;
private:
String m_schema_name;
bool m_is_error_if_schema_exists;
};
class CreateTable : public Statement {
public:
CreateTable(String schema_name, String table_name, RefPtr<Select> select_statement, bool is_temporary, bool is_error_if_table_exists)
@ -703,6 +723,8 @@ public:
bool is_temporary() const { return m_is_temporary; }
bool is_error_if_table_exists() const { return m_is_error_if_table_exists; }
RefPtr<SQLResult> execute(NonnullRefPtr<Database>) const override;
private:
String m_schema_name;
String m_table_name;

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibSQL/AST/AST.h>
#include <LibSQL/Database.h>
#include <LibSQL/Meta.h>
namespace SQL::AST {
RefPtr<SQLResult> CreateSchema::execute(NonnullRefPtr<Database> database) const
{
auto schema_def = database->get_schema(m_schema_name);
if (schema_def) {
if (m_is_error_if_schema_exists) {
return SQLResult::construct(SQLCommand::Create, SQLErrorCode::SchemaExists, m_schema_name);
}
return SQLResult::construct(SQLCommand::Create);
}
schema_def = SchemaDef::construct(m_schema_name);
database->add_schema(*schema_def);
return SQLResult::construct(SQLCommand::Create, 0, 1);
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibSQL/AST/AST.h>
#include <LibSQL/Database.h>
namespace SQL::AST {
RefPtr<SQLResult> CreateTable::execute(NonnullRefPtr<Database> database) const
{
auto schema_name = (!m_schema_name.is_null() && !m_schema_name.is_empty()) ? m_schema_name : "default";
auto schema_def = database->get_schema(schema_name);
if (!schema_def)
return SQLResult::construct(SQLCommand::Create, SQLErrorCode::SchemaDoesNotExist, m_schema_name);
auto table_def = database->get_table(schema_name, m_table_name);
if (table_def) {
if (m_is_error_if_table_exists) {
return SQLResult::construct(SQLCommand::Create, SQLErrorCode::TableExists, m_table_name);
} else {
return SQLResult::construct(SQLCommand::Create);
}
}
table_def = TableDef::construct(schema_def, m_table_name);
for (auto& column : m_columns) {
SQLType type;
if (column.type_name()->name() == "VARCHAR" || column.type_name()->name() == "TEXT") {
type = SQLType::Text;
} else if (column.type_name()->name() == "INT" || column.type_name()->name() == "INTEGER") {
type = SQLType::Integer;
} else if (column.type_name()->name() == "FLOAT" || column.type_name()->name() == "NUMBER") {
type = SQLType::Float;
} else {
return SQLResult::construct(SQLCommand::Create, SQLErrorCode::InvalidType, column.type_name()->name());
}
table_def->append_column(column.name(), type);
}
database->add_table(*table_def);
return SQLResult::construct(SQLCommand::Create, 0, 1);
}
}

View file

@ -37,7 +37,11 @@ NonnullRefPtr<Statement> Parser::parse_statement()
{
switch (m_parser_state.m_token.type()) {
case TokenType::Create:
return parse_create_table_statement();
consume();
if (match(TokenType::Schema))
return parse_create_schema_statement();
else
return parse_create_table_statement();
case TokenType::Alter:
return parse_alter_table_statement();
case TokenType::Drop:
@ -73,10 +77,24 @@ NonnullRefPtr<Statement> Parser::parse_statement_with_expression_list(RefPtr<Com
}
}
NonnullRefPtr<CreateSchema> Parser::parse_create_schema_statement()
{
consume(TokenType::Schema);
bool is_error_if_exists = true;
if (consume_if(TokenType::If)) {
consume(TokenType::Not);
consume(TokenType::Exists);
is_error_if_exists = false;
}
String schema_name = consume(TokenType::Identifier).value();
return create_ast_node<CreateSchema>(move(schema_name), is_error_if_exists);
}
NonnullRefPtr<CreateTable> Parser::parse_create_table_statement()
{
// https://sqlite.org/lang_createtable.html
consume(TokenType::Create);
bool is_temporary = false;
if (consume_if(TokenType::Temp) || consume_if(TokenType::Temporary))

View file

@ -55,6 +55,7 @@ private:
NonnullRefPtr<Statement> parse_statement();
NonnullRefPtr<Statement> parse_statement_with_expression_list(RefPtr<CommonTableExpressionList>);
NonnullRefPtr<CreateSchema> parse_create_schema_statement();
NonnullRefPtr<CreateTable> parse_create_table_statement();
NonnullRefPtr<AlterTable> parse_alter_table_statement();
NonnullRefPtr<DropTable> parse_drop_table_statement();

View file

@ -138,6 +138,7 @@ namespace SQL::AST {
__ENUMERATE_SQL_TOKEN("ROW", Row, Keyword) \
__ENUMERATE_SQL_TOKEN("ROWS", Rows, Keyword) \
__ENUMERATE_SQL_TOKEN("SAVEPOINT", Savepoint, Keyword) \
__ENUMERATE_SQL_TOKEN("SCHEMA", Schema, Keyword) \
__ENUMERATE_SQL_TOKEN("SELECT", Select, Keyword) \
__ENUMERATE_SQL_TOKEN("SET", Set, Keyword) \
__ENUMERATE_SQL_TOKEN("TABLE", Table, Keyword) \