Parcourir la source

LibSQL: Partially implement the DELETE command

This implements enough to delete rows filtered by a WHERE clause.
Timothy Flynn il y a 2 ans
Parent
commit
aba7f11a50

+ 118 - 0
Tests/LibSQL/TestSqlStatementExecution.cpp

@@ -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());
+    }
+}
+
 }
 }

+ 2 - 0
Userland/Libraries/LibSQL/AST/AST.h

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

+ 39 - 0
Userland/Libraries/LibSQL/AST/Delete.cpp

@@ -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;
+}
+
+}

+ 1 - 0
Userland/Libraries/LibSQL/CMakeLists.txt

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

+ 29 - 0
Userland/Libraries/LibSQL/Database.cpp

@@ -215,6 +215,35 @@ ErrorOr<void> Database::insert(Row& row)
     return {};
     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)
 ErrorOr<void> Database::update(Row& tuple)
 {
 {
     VERIFY(m_table_cache.get(tuple.table().key().hash()).has_value());
     VERIFY(m_table_cache.get(tuple.table().key().hash()).has_value());

+ 1 - 0
Userland/Libraries/LibSQL/Database.h

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