LibSQL: Partially implement the DELETE command

This implements enough to delete rows filtered by a WHERE clause.
This commit is contained in:
Timothy Flynn 2022-11-28 07:49:03 -05:00 committed by Linus Groh
parent 6d3f68cc11
commit aba7f11a50
6 changed files with 190 additions and 0 deletions

View file

@ -768,4 +768,122 @@ TEST_CASE(describe_large_table_after_persist)
}
}
TEST_CASE(delete_single_row)
{
ScopeGuard guard([]() { unlink(db_name); });
{
auto database = SQL::Database::construct(db_name);
EXPECT(!database->open().is_error());
create_table(database);
for (auto count = 0; count < 10; ++count) {
auto result = execute(database, String::formatted("INSERT INTO TestSchema.TestTable VALUES ( 'T{}', {} );", count, count));
EXPECT_EQ(result.size(), 1u);
}
auto result = execute(database, "SELECT * FROM TestSchema.TestTable;");
EXPECT_EQ(result.size(), 10u);
}
{
auto database = SQL::Database::construct(db_name);
EXPECT(!database->open().is_error());
execute(database, "DELETE FROM TestSchema.TestTable WHERE (IntColumn = 4);");
auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;");
EXPECT_EQ(result.size(), 9u);
for (auto i = 0u; i < 4; ++i)
EXPECT_EQ(result[i].row[0], i);
for (auto i = 5u; i < 9; ++i)
EXPECT_EQ(result[i].row[0], i + 1);
}
{
auto database = SQL::Database::construct(db_name);
EXPECT(!database->open().is_error());
auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;");
EXPECT_EQ(result.size(), 9u);
for (auto i = 0u; i < 4; ++i)
EXPECT_EQ(result[i].row[0], i);
for (auto i = 5u; i < 9; ++i)
EXPECT_EQ(result[i].row[0], i + 1);
}
}
TEST_CASE(delete_multiple_rows)
{
ScopeGuard guard([]() { unlink(db_name); });
{
auto database = SQL::Database::construct(db_name);
EXPECT(!database->open().is_error());
create_table(database);
for (auto count = 0; count < 10; ++count) {
auto result = execute(database, String::formatted("INSERT INTO TestSchema.TestTable VALUES ( 'T{}', {} );", count, count));
EXPECT_EQ(result.size(), 1u);
}
auto result = execute(database, "SELECT * FROM TestSchema.TestTable;");
EXPECT_EQ(result.size(), 10u);
}
{
auto database = SQL::Database::construct(db_name);
EXPECT(!database->open().is_error());
execute(database, "DELETE FROM TestSchema.TestTable WHERE (IntColumn >= 4);");
auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;");
EXPECT_EQ(result.size(), 4u);
for (auto i = 0u; i < result.size(); ++i)
EXPECT_EQ(result[i].row[0], i);
}
{
auto database = SQL::Database::construct(db_name);
EXPECT(!database->open().is_error());
auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;");
EXPECT_EQ(result.size(), 4u);
for (auto i = 0u; i < result.size(); ++i)
EXPECT_EQ(result[i].row[0], i);
}
}
TEST_CASE(delete_all_rows)
{
ScopeGuard guard([]() { unlink(db_name); });
{
auto database = SQL::Database::construct(db_name);
EXPECT(!database->open().is_error());
create_table(database);
for (auto count = 0; count < 10; ++count) {
auto result = execute(database, String::formatted("INSERT INTO TestSchema.TestTable VALUES ( 'T{}', {} );", count, count));
EXPECT_EQ(result.size(), 1u);
}
auto result = execute(database, "SELECT * FROM TestSchema.TestTable;");
EXPECT_EQ(result.size(), 10u);
}
{
auto database = SQL::Database::construct(db_name);
EXPECT(!database->open().is_error());
execute(database, "DELETE FROM TestSchema.TestTable;");
auto result = execute(database, "SELECT * FROM TestSchema.TestTable;");
EXPECT(result.is_empty());
}
{
auto database = SQL::Database::construct(db_name);
EXPECT(!database->open().is_error());
auto result = execute(database, "SELECT * FROM TestSchema.TestTable;");
EXPECT(result.is_empty());
}
}
}

View file

@ -1017,6 +1017,8 @@ public:
RefPtr<Expression> const& where_clause() const { return m_where_clause; }
RefPtr<ReturningClause> const& returning_clause() const { return m_returning_clause; }
virtual ResultOr<ResultSet> execute(ExecutionContext&) const override;
private:
RefPtr<CommonTableExpressionList> m_common_table_expression_list;
NonnullRefPtr<QualifiedTableName> m_qualified_table_name;

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibSQL/AST/AST.h>
#include <LibSQL/Database.h>
#include <LibSQL/Meta.h>
#include <LibSQL/Row.h>
namespace SQL::AST {
ResultOr<ResultSet> Delete::execute(ExecutionContext& context) const
{
auto const& schema_name = m_qualified_table_name->schema_name();
auto const& table_name = m_qualified_table_name->table_name();
auto table_def = TRY(context.database->get_table(schema_name, table_name));
ResultSet result { SQLCommand::Delete };
for (auto& table_row : TRY(context.database->select_all(*table_def))) {
context.current_row = &table_row;
if (auto const& where_clause = this->where_clause()) {
auto where_result = TRY(where_clause->evaluate(context)).to_bool();
if (!where_result.has_value() || !where_result.value())
continue;
}
TRY(context.database->remove(table_row));
// FIXME: Implement the RETURNING clause.
}
return result;
}
}

View file

@ -1,6 +1,7 @@
set(SOURCES
AST/CreateSchema.cpp
AST/CreateTable.cpp
AST/Delete.cpp
AST/Describe.cpp
AST/Expression.cpp
AST/Insert.cpp

View file

@ -215,6 +215,35 @@ ErrorOr<void> Database::insert(Row& row)
return {};
}
ErrorOr<void> Database::remove(Row& row)
{
auto& table = row.table();
VERIFY(m_table_cache.get(table.key().hash()).has_value());
if (table.pointer() == row.pointer()) {
auto table_key = table.key();
table_key.set_pointer(row.next_pointer());
m_tables->update_key_pointer(table_key);
table.set_pointer(row.next_pointer());
return {};
}
for (auto pointer = table.pointer(); pointer;) {
auto current = m_serializer.deserialize_block<Row>(pointer, table, pointer);
if (current.next_pointer() == row.pointer()) {
current.set_next_pointer(row.next_pointer());
TRY(update(current));
break;
}
pointer = current.next_pointer();
}
return {};
}
ErrorOr<void> Database::update(Row& tuple)
{
VERIFY(m_table_cache.get(tuple.table().key().hash()).has_value());

View file

@ -44,6 +44,7 @@ public:
ErrorOr<Vector<Row>> select_all(TableDef const&);
ErrorOr<Vector<Row>> match(TableDef const&, Key const&);
ErrorOr<void> insert(Row&);
ErrorOr<void> remove(Row&);
ErrorOr<void> update(Row&);
private: