mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-21 02:08:12 +03: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:
parent
30691549fd
commit
1037d6b0eb
Notes:
sideshowbarker
2024-07-18 10:04:39 +09:00
Author: https://github.com/JanDeVisser Commit: https://github.com/SerenityOS/serenity/commit/1037d6b0eb4 Pull-request: https://github.com/SerenityOS/serenity/pull/8306 Reviewed-by: https://github.com/MaxWipfli Reviewed-by: https://github.com/alimpfard ✅ Reviewed-by: https://github.com/trflynn89 ✅
@ -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;
|
||||
|
28
Userland/Libraries/LibSQL/AST/CreateSchema.cpp
Normal file
28
Userland/Libraries/LibSQL/AST/CreateSchema.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
44
Userland/Libraries/LibSQL/AST/CreateTable.cpp
Normal file
44
Userland/Libraries/LibSQL/AST/CreateTable.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
@ -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))
|
||||
|
@ -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();
|
||||
|
@ -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) \
|
||||
|
@ -1,21 +1,23 @@
|
||||
set(SOURCES
|
||||
AST/Lexer.cpp
|
||||
AST/Parser.cpp
|
||||
AST/SyntaxHighlighter.cpp
|
||||
AST/Token.cpp
|
||||
BTree.cpp
|
||||
BTreeIterator.cpp
|
||||
Database.cpp
|
||||
HashIndex.cpp
|
||||
Heap.cpp
|
||||
Index.cpp
|
||||
Key.cpp
|
||||
Meta.cpp
|
||||
Row.cpp
|
||||
TreeNode.cpp
|
||||
Tuple.cpp
|
||||
Value.cpp
|
||||
)
|
||||
AST/CreateSchema.cpp
|
||||
AST/CreateTable.cpp
|
||||
AST/Lexer.cpp
|
||||
AST/Parser.cpp
|
||||
AST/SyntaxHighlighter.cpp
|
||||
AST/Token.cpp
|
||||
BTree.cpp
|
||||
BTreeIterator.cpp
|
||||
Database.cpp
|
||||
HashIndex.cpp
|
||||
Heap.cpp
|
||||
Index.cpp
|
||||
Key.cpp
|
||||
Meta.cpp
|
||||
Row.cpp
|
||||
TreeNode.cpp
|
||||
Tuple.cpp
|
||||
Value.cpp
|
||||
)
|
||||
|
||||
serenity_lib(LibSQL sql)
|
||||
target_link_libraries(LibSQL LibCore LibSyntax)
|
||||
|
@ -32,6 +32,11 @@ Database::Database(String name)
|
||||
m_table_columns->on_new_root = [&]() {
|
||||
m_heap->set_table_columns_root(m_table_columns->root());
|
||||
};
|
||||
auto default_schema = get_schema("default");
|
||||
if (!default_schema) {
|
||||
default_schema = SchemaDef::construct("default");
|
||||
add_schema(*default_schema);
|
||||
}
|
||||
}
|
||||
|
||||
void Database::add_schema(SchemaDef const& schema)
|
||||
@ -54,7 +59,6 @@ RefPtr<SchemaDef> Database::get_schema(String const& schema_name)
|
||||
return schema_def_opt.value();
|
||||
auto schema_iterator = m_schemas->find(key);
|
||||
if (schema_iterator.is_end() || (*schema_iterator != key)) {
|
||||
warnln("Schema {} not found", schema_name);
|
||||
return nullptr;
|
||||
}
|
||||
auto ret = SchemaDef::construct(*schema_iterator);
|
||||
@ -85,7 +89,6 @@ RefPtr<TableDef> Database::get_table(String const& schema, String const& name)
|
||||
return table_def_opt.value();
|
||||
auto table_iterator = m_tables->find(key);
|
||||
if (table_iterator.is_end() || (*table_iterator != key)) {
|
||||
warnln("Table {} not found", name);
|
||||
return nullptr;
|
||||
}
|
||||
auto schema_def = get_schema(schema);
|
||||
|
@ -15,20 +15,15 @@
|
||||
namespace SQL {
|
||||
|
||||
/**
|
||||
* A Key is an element of a random-access data structure persisted in a Heap.
|
||||
* Key objects stored in such a structure have a definition controlling the
|
||||
* number of parts or columns the key has, the types of the parts, and the
|
||||
* sort order of these parts. Besides having an optional definition, a Key
|
||||
* consists of one Value object per part. In addition, keys have a u32 pointer
|
||||
* A Tuple is an element of a random-access data structure persisted in a Heap.
|
||||
* Tuple objects stored in such a structure have a definition controlling the
|
||||
* number of parts or columns the tuple has, the types of the parts, and the
|
||||
* sort order of these parts. Besides having an optional definition, a Tuple
|
||||
* consists of one Value object per part. In addition, tuples have a u32 pointer
|
||||
* member which points to a Heap location.
|
||||
*
|
||||
* Key objects without a definition can be used to locate/find objects in
|
||||
* a searchable data collection.
|
||||
*
|
||||
* FIXME Currently the Key definition is passed as an `IndexDefinition` meta
|
||||
* data object, meaning that names are associated with both the definition
|
||||
* and the parts of the key. These names are not used, meaning that key
|
||||
* definitions should probably be constructed in a different way.
|
||||
* Tuple is a base class; concrete subclasses are Key, which implements the
|
||||
* elements of an index, and Row, which implements the rows in a table.
|
||||
*/
|
||||
class Tuple {
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user