diff --git a/Tests/LibSQL/TestSqlBtreeIndex.cpp b/Tests/LibSQL/TestSqlBtreeIndex.cpp index 726edfad94..edb3a6b722 100644 --- a/Tests/LibSQL/TestSqlBtreeIndex.cpp +++ b/Tests/LibSQL/TestSqlBtreeIndex.cpp @@ -127,7 +127,7 @@ void insert_into_and_scan_btree(int num_keys); NonnullRefPtr setup_btree(SQL::Heap& heap) { SQL::TupleDescriptor tuple_descriptor; - tuple_descriptor.append({ "key_value", SQL::SQLType::Integer, SQL::AST::Order::Ascending }); + tuple_descriptor.append({ "key_value", SQL::SQLType::Integer, SQL::Order::Ascending }); auto root_pointer = heap.user_value(0); if (!root_pointer) { diff --git a/Tests/LibSQL/TestSqlHashIndex.cpp b/Tests/LibSQL/TestSqlHashIndex.cpp index ca4cab3ee2..c6f0b5badc 100644 --- a/Tests/LibSQL/TestSqlHashIndex.cpp +++ b/Tests/LibSQL/TestSqlHashIndex.cpp @@ -124,8 +124,8 @@ void insert_into_and_scan_hash_index(int num_keys); NonnullRefPtr setup_hash_index(SQL::Heap& heap) { SQL::TupleDescriptor tuple_descriptor; - tuple_descriptor.append({ "key_value", SQL::SQLType::Integer, SQL::AST::Order::Ascending }); - tuple_descriptor.append({ "text_value", SQL::SQLType::Text, SQL::AST::Order::Ascending }); + tuple_descriptor.append({ "key_value", SQL::SQLType::Integer, SQL::Order::Ascending }); + tuple_descriptor.append({ "text_value", SQL::SQLType::Text, SQL::Order::Ascending }); auto directory_pointer = heap.user_value(0); if (!directory_pointer) { diff --git a/Tests/LibSQL/TestSqlStatementParser.cpp b/Tests/LibSQL/TestSqlStatementParser.cpp index 98c793f936..0b1194fd66 100644 --- a/Tests/LibSQL/TestSqlStatementParser.cpp +++ b/Tests/LibSQL/TestSqlStatementParser.cpp @@ -567,8 +567,8 @@ TEST_CASE(select) struct Ordering { String collation_name; - SQL::AST::Order order; - SQL::AST::Nulls nulls; + SQL::Order order; + SQL::Nulls nulls; }; auto validate = [](StringView sql, Vector expected_columns, Vector expected_from_list, bool expect_where_clause, size_t expected_group_by_size, bool expect_having_clause, Vector expected_ordering, bool expect_limit_clause, bool expect_offset_clause) { @@ -674,13 +674,13 @@ TEST_CASE(select) validate("SELECT * FROM table_name GROUP BY column1, column2, column3;", all, from, false, 3, false, {}, false, false); validate("SELECT * FROM table_name GROUP BY column_name HAVING 'abc';", all, from, false, 1, true, {}, false, false); - validate("SELECT * FROM table_name ORDER BY column_name;", all, from, false, 0, false, { { {}, SQL::AST::Order::Ascending, SQL::AST::Nulls::First } }, false, false); - validate("SELECT * FROM table_name ORDER BY column_name COLLATE collation;", all, from, false, 0, false, { { "COLLATION", SQL::AST::Order::Ascending, SQL::AST::Nulls::First } }, false, false); - validate("SELECT * FROM table_name ORDER BY column_name ASC;", all, from, false, 0, false, { { {}, SQL::AST::Order::Ascending, SQL::AST::Nulls::First } }, false, false); - validate("SELECT * FROM table_name ORDER BY column_name DESC;", all, from, false, 0, false, { { {}, SQL::AST::Order::Descending, SQL::AST::Nulls::Last } }, false, false); - validate("SELECT * FROM table_name ORDER BY column_name ASC NULLS LAST;", all, from, false, 0, false, { { {}, SQL::AST::Order::Ascending, SQL::AST::Nulls::Last } }, false, false); - validate("SELECT * FROM table_name ORDER BY column_name DESC NULLS FIRST;", all, from, false, 0, false, { { {}, SQL::AST::Order::Descending, SQL::AST::Nulls::First } }, false, false); - validate("SELECT * FROM table_name ORDER BY column1, column2 DESC, column3 NULLS LAST;", all, from, false, 0, false, { { {}, SQL::AST::Order::Ascending, SQL::AST::Nulls::First }, { {}, SQL::AST::Order::Descending, SQL::AST::Nulls::Last }, { {}, SQL::AST::Order::Ascending, SQL::AST::Nulls::Last } }, false, false); + validate("SELECT * FROM table_name ORDER BY column_name;", all, from, false, 0, false, { { {}, SQL::Order::Ascending, SQL::Nulls::First } }, false, false); + validate("SELECT * FROM table_name ORDER BY column_name COLLATE collation;", all, from, false, 0, false, { { "COLLATION", SQL::Order::Ascending, SQL::Nulls::First } }, false, false); + validate("SELECT * FROM table_name ORDER BY column_name ASC;", all, from, false, 0, false, { { {}, SQL::Order::Ascending, SQL::Nulls::First } }, false, false); + validate("SELECT * FROM table_name ORDER BY column_name DESC;", all, from, false, 0, false, { { {}, SQL::Order::Descending, SQL::Nulls::Last } }, false, false); + validate("SELECT * FROM table_name ORDER BY column_name ASC NULLS LAST;", all, from, false, 0, false, { { {}, SQL::Order::Ascending, SQL::Nulls::Last } }, false, false); + validate("SELECT * FROM table_name ORDER BY column_name DESC NULLS FIRST;", all, from, false, 0, false, { { {}, SQL::Order::Descending, SQL::Nulls::First } }, false, false); + validate("SELECT * FROM table_name ORDER BY column1, column2 DESC, column3 NULLS LAST;", all, from, false, 0, false, { { {}, SQL::Order::Ascending, SQL::Nulls::First }, { {}, SQL::Order::Descending, SQL::Nulls::Last }, { {}, SQL::Order::Ascending, SQL::Nulls::Last } }, false, false); validate("SELECT * FROM table_name LIMIT 15;", all, from, false, 0, false, {}, true, false); validate("SELECT * FROM table_name LIMIT 15 OFFSET 16;", all, from, false, 0, false, {}, true, true); diff --git a/Tests/LibSQL/TestSqlValueAndTuple.cpp b/Tests/LibSQL/TestSqlValueAndTuple.cpp index 8eeeb2723f..7740e5ce48 100644 --- a/Tests/LibSQL/TestSqlValueAndTuple.cpp +++ b/Tests/LibSQL/TestSqlValueAndTuple.cpp @@ -136,8 +136,8 @@ TEST_CASE(order_int_values) TEST_CASE(tuple) { SQL::TupleDescriptor descriptor; - descriptor.append({ "col1", SQL::SQLType::Text, SQL::AST::Order::Ascending }); - descriptor.append({ "col2", SQL::SQLType::Integer, SQL::AST::Order::Descending }); + descriptor.append({ "col1", SQL::SQLType::Text, SQL::Order::Ascending }); + descriptor.append({ "col2", SQL::SQLType::Integer, SQL::Order::Descending }); SQL::Tuple tuple(descriptor); tuple["col1"] = "Test"; @@ -149,8 +149,8 @@ TEST_CASE(tuple) TEST_CASE(serialize_tuple) { SQL::TupleDescriptor descriptor; - descriptor.append({ "col1", SQL::SQLType::Text, SQL::AST::Order::Ascending }); - descriptor.append({ "col2", SQL::SQLType::Integer, SQL::AST::Order::Descending }); + descriptor.append({ "col1", SQL::SQLType::Text, SQL::Order::Ascending }); + descriptor.append({ "col2", SQL::SQLType::Integer, SQL::Order::Descending }); SQL::Tuple tuple(descriptor); tuple["col1"] = "Test"; @@ -170,8 +170,8 @@ TEST_CASE(serialize_tuple) TEST_CASE(copy_tuple) { SQL::TupleDescriptor descriptor; - descriptor.append({ "col1", SQL::SQLType::Text, SQL::AST::Order::Ascending }); - descriptor.append({ "col2", SQL::SQLType::Integer, SQL::AST::Order::Descending }); + descriptor.append({ "col1", SQL::SQLType::Text, SQL::Order::Ascending }); + descriptor.append({ "col2", SQL::SQLType::Integer, SQL::Order::Descending }); SQL::Tuple tuple(descriptor); tuple["col1"] = "Test"; @@ -188,8 +188,8 @@ TEST_CASE(copy_tuple) TEST_CASE(compare_tuples) { SQL::TupleDescriptor descriptor; - descriptor.append({ "col1", SQL::SQLType::Text, SQL::AST::Order::Ascending }); - descriptor.append({ "col2", SQL::SQLType::Integer, SQL::AST::Order::Descending }); + descriptor.append({ "col1", SQL::SQLType::Text, SQL::Order::Ascending }); + descriptor.append({ "col2", SQL::SQLType::Integer, SQL::Order::Descending }); SQL::Tuple tuple1(descriptor); tuple1["col1"] = "Test"; diff --git a/Userland/Libraries/LibSQL/AST/AST.h b/Userland/Libraries/LibSQL/AST/AST.h index 8f22bbab65..855c0bbd26 100644 --- a/Userland/Libraries/LibSQL/AST/AST.h +++ b/Userland/Libraries/LibSQL/AST/AST.h @@ -13,6 +13,8 @@ #include #include #include +#include +#include namespace SQL::AST { @@ -251,16 +253,6 @@ private: NonnullRefPtrVector m_subqueries {}; }; -enum class Order { - Ascending, - Descending, -}; - -enum class Nulls { - First, - Last, -}; - class OrderingTerm : public ASTNode { public: OrderingTerm(NonnullRefPtr expression, String collation_name, Order order, Nulls nulls) diff --git a/Userland/Libraries/LibSQL/Forward.h b/Userland/Libraries/LibSQL/Forward.h index e844a1e782..1ef5e733be 100644 --- a/Userland/Libraries/LibSQL/Forward.h +++ b/Userland/Libraries/LibSQL/Forward.h @@ -22,6 +22,7 @@ class IndexDef; class Key; class KeyPartDef; class Row; +class SQLResult; class TableDef; class TreeNode; class Tuple; diff --git a/Userland/Libraries/LibSQL/Meta.cpp b/Userland/Libraries/LibSQL/Meta.cpp index ce8dc12bd2..43a240f9af 100644 --- a/Userland/Libraries/LibSQL/Meta.cpp +++ b/Userland/Libraries/LibSQL/Meta.cpp @@ -37,7 +37,7 @@ NonnullRefPtr SchemaDef::index_def() { NonnullRefPtr s_index_def = IndexDef::construct("$schema", true, 0); if (!s_index_def->size()) { - s_index_def->append_column("schema_name", SQLType::Text, AST::Order::Ascending); + s_index_def->append_column("schema_name", SQLType::Text, Order::Ascending); } return s_index_def; } @@ -70,15 +70,15 @@ NonnullRefPtr ColumnDef::index_def() { NonnullRefPtr s_index_def = IndexDef::construct("$column", true, 0); if (!s_index_def->size()) { - s_index_def->append_column("table_hash", SQLType::Integer, AST::Order::Ascending); - s_index_def->append_column("column_number", SQLType::Integer, AST::Order::Ascending); - s_index_def->append_column("column_name", SQLType::Text, AST::Order::Ascending); - s_index_def->append_column("column_type", SQLType::Integer, AST::Order::Ascending); + s_index_def->append_column("table_hash", SQLType::Integer, Order::Ascending); + s_index_def->append_column("column_number", SQLType::Integer, Order::Ascending); + s_index_def->append_column("column_name", SQLType::Text, Order::Ascending); + s_index_def->append_column("column_type", SQLType::Integer, Order::Ascending); } return s_index_def; } -KeyPartDef::KeyPartDef(IndexDef* index, String name, SQLType sql_type, AST::Order sort_order) +KeyPartDef::KeyPartDef(IndexDef* index, String name, SQLType sql_type, Order sort_order) : ColumnDef(index, index->size(), move(name), sql_type) , m_sort_order(sort_order) { @@ -96,7 +96,7 @@ IndexDef::IndexDef(String name, bool unique, u32 pointer) { } -void IndexDef::append_column(String name, SQLType sql_type, AST::Order sort_order) +void IndexDef::append_column(String name, SQLType sql_type, Order sort_order) { auto part = KeyPartDef::construct(this, move(name), sql_type, sort_order); m_key_definition.append(part); @@ -131,9 +131,9 @@ NonnullRefPtr IndexDef::index_def() { NonnullRefPtr s_index_def = IndexDef::construct("$index", true, 0); if (!s_index_def->size()) { - s_index_def->append_column("table_hash", SQLType::Integer, AST::Order::Ascending); - s_index_def->append_column("index_name", SQLType::Text, AST::Order::Ascending); - s_index_def->append_column("unique", SQLType::Integer, AST::Order::Ascending); + s_index_def->append_column("table_hash", SQLType::Integer, Order::Ascending); + s_index_def->append_column("index_name", SQLType::Text, Order::Ascending); + s_index_def->append_column("unique", SQLType::Integer, Order::Ascending); } return s_index_def; } @@ -149,7 +149,7 @@ TupleDescriptor TableDef::to_tuple_descriptor() const { TupleDescriptor ret; for (auto& part : m_columns) { - ret.append({ part.name(), part.type(), AST::Order::Ascending }); + ret.append({ part.name(), part.type(), Order::Ascending }); } return ret; } @@ -192,8 +192,8 @@ NonnullRefPtr TableDef::index_def() { NonnullRefPtr s_index_def = IndexDef::construct("$table", true, 0); if (!s_index_def->size()) { - s_index_def->append_column("schema_hash", SQLType::Integer, AST::Order::Ascending); - s_index_def->append_column("table_name", SQLType::Text, AST::Order::Ascending); + s_index_def->append_column("schema_hash", SQLType::Integer, Order::Ascending); + s_index_def->append_column("table_name", SQLType::Text, Order::Ascending); } return s_index_def; } diff --git a/Userland/Libraries/LibSQL/Meta.h b/Userland/Libraries/LibSQL/Meta.h index 468e34cb95..ed2737e46e 100644 --- a/Userland/Libraries/LibSQL/Meta.h +++ b/Userland/Libraries/LibSQL/Meta.h @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -90,11 +89,11 @@ class KeyPartDef : public ColumnDef { C_OBJECT(KeyPartDef); public: - KeyPartDef(IndexDef*, String, SQLType, AST::Order = AST::Order::Ascending); - AST::Order sort_order() const { return m_sort_order; } + KeyPartDef(IndexDef*, String, SQLType, Order = Order::Ascending); + Order sort_order() const { return m_sort_order; } private: - AST::Order m_sort_order { AST::Order::Ascending }; + Order m_sort_order { Order::Ascending }; }; class IndexDef : public Relation { @@ -106,7 +105,7 @@ public: NonnullRefPtrVector key_definition() const { return m_key_definition; } bool unique() const { return m_unique; } [[nodiscard]] size_t size() const { return m_key_definition.size(); } - void append_column(String, SQLType, AST::Order = AST::Order::Ascending); + void append_column(String, SQLType, Order = Order::Ascending); Key key() const override; [[nodiscard]] TupleDescriptor to_tuple_descriptor() const; static NonnullRefPtr index_def(); diff --git a/Userland/Libraries/LibSQL/SQLResult.h b/Userland/Libraries/LibSQL/SQLResult.h new file mode 100644 index 0000000000..0c83b19b40 --- /dev/null +++ b/Userland/Libraries/LibSQL/SQLResult.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2021, Jan de Visser + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace SQL { + +#define ENUMERATE_SQL_COMMANDS(S) \ + S(Create) \ + S(Delete) \ + S(Insert) \ + S(Select) \ + S(Update) + +enum class SQLCommand { +#undef __ENUMERATE_SQL_COMMAND +#define __ENUMERATE_SQL_COMMAND(command) command, + ENUMERATE_SQL_COMMANDS(__ENUMERATE_SQL_COMMAND) +#undef __ENUMERATE_SQL_COMMAND +}; + +constexpr char const* command_tag(SQLCommand command) +{ + switch (command) { +#undef __ENUMERATE_SQL_COMMAND +#define __ENUMERATE_SQL_COMMAND(command) \ + case SQLCommand::command: \ + return #command; + ENUMERATE_SQL_COMMANDS(__ENUMERATE_SQL_COMMAND) +#undef __ENUMERATE_SQL_COMMAND + } +} + +#define ENUMERATE_SQL_ERRORS(S) \ + S(NoError, "No error") \ + S(DatabaseUnavailable, "Database Unavailable") \ + S(StatementUnavailable, "Statement with id {} Unavailable") \ + S(SyntaxError, "Syntax Error") \ + S(DatabaseDoesNotExist, "Database {} does not exist") \ + S(SchemaDoesNotExist, "Schema {} does not exist") \ + S(SchemaExists, "Schema {} already exist") \ + S(TableDoesNotExist, "Table {} does not exist") \ + S(TableExists, "Table {} already exist") \ + S(InvalidType, "Invalid type {}") + +enum class SQLErrorCode { +#undef __ENUMERATE_SQL_ERROR +#define __ENUMERATE_SQL_ERROR(error, description) error, + ENUMERATE_SQL_ERRORS(__ENUMERATE_SQL_ERROR) +#undef __ENUMERATE_SQL_ERROR +}; + +struct SQLError { + SQLErrorCode code { SQLErrorCode::NoError }; + String error_argument { "" }; + + String to_string() const + { + String code_string; + String message; + switch (code) { +#undef __ENUMERATE_SQL_ERROR +#define __ENUMERATE_SQL_ERROR(error, description) \ + case SQLErrorCode::error: \ + code_string = #error; \ + message = description; \ + break; + ENUMERATE_SQL_ERRORS(__ENUMERATE_SQL_ERROR) +#undef __ENUMERATE_SQL_ERROR + default: + VERIFY_NOT_REACHED(); + } + if (!error_argument.is_null() && !error_argument.is_empty()) { + if (message.find("{}").has_value()) { + message = String::formatted(message, error_argument); + } else { + message = String::formatted("{}: {}", message, error_argument); + } + } + if (message.is_null() || (message.is_empty())) { + return code_string; + } else { + return String::formatted("{}: {}", code_string, message); + } + } +}; + +class SQLResult : public Core::Object { + C_OBJECT(SQLResult) + +public: + void append(Tuple const& tuple) + { + m_has_results = true; + m_result_set.append(tuple); + } + + SQLCommand command() const { return m_command; } + int updated() const { return m_update_count; } + int inserted() const { return m_insert_count; } + int deleted() const { return m_delete_count; } + SQLError const& error() const { return m_error; } + bool has_results() const { return m_has_results; } + Vector const& results() const { return m_result_set; } + +private: + SQLResult() = default; + + explicit SQLResult(SQLCommand command, int update_count = 0, int insert_count = 0, int delete_count = 0) + : m_command(command) + , m_update_count(update_count) + , m_insert_count(insert_count) + , m_delete_count(delete_count) + { + } + + SQLResult(SQLCommand command, SQLErrorCode error_code, String error_argument) + : m_command(command) + , m_error({ error_code, move(error_argument) }) + { + } + + SQLCommand m_command { SQLCommand::Select }; + SQLError m_error { SQLErrorCode::NoError, "" }; + int m_update_count { 0 }; + int m_insert_count { 0 }; + int m_delete_count { 0 }; + bool m_has_results { false }; + Vector m_result_set; +}; + +} diff --git a/Userland/Libraries/LibSQL/Tuple.cpp b/Userland/Libraries/LibSQL/Tuple.cpp index c9d5da5c1f..aee5788d1f 100644 --- a/Userland/Libraries/LibSQL/Tuple.cpp +++ b/Userland/Libraries/LibSQL/Tuple.cpp @@ -174,6 +174,15 @@ String Tuple::to_string() const return builder.build(); } +Vector Tuple::to_string_vector() const +{ + Vector ret; + for (auto& value : m_data) { + ret.append(value.to_string().value()); + } + return ret; +} + size_t Tuple::size() const { size_t sz = sizeof(u32); @@ -203,7 +212,7 @@ int Tuple::compare(const Tuple& other) const for (auto ix = 0u; ix < num_values; ix++) { auto ret = m_data[ix].compare(other.m_data[ix]); if (ret != 0) { - if ((ix < m_descriptor.size()) && m_descriptor[ix].order == AST::Order::Descending) + if ((ix < m_descriptor.size()) && m_descriptor[ix].order == Order::Descending) ret = -ret; return ret; } @@ -223,7 +232,7 @@ int Tuple::match(const Tuple& other) const return -1; auto ret = m_data[my_index.value()].compare(other_value); if (ret != 0) - return (m_descriptor[my_index.value()].order == AST::Order::Descending) ? -ret : ret; + return (m_descriptor[my_index.value()].order == Order::Descending) ? -ret : ret; other_index++; } return 0; diff --git a/Userland/Libraries/LibSQL/Tuple.h b/Userland/Libraries/LibSQL/Tuple.h index 19dc2d4dd9..20fb3525e7 100644 --- a/Userland/Libraries/LibSQL/Tuple.h +++ b/Userland/Libraries/LibSQL/Tuple.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include #include @@ -42,6 +43,7 @@ public: [[nodiscard]] String to_string() const; explicit operator String() const { return to_string(); } + [[nodiscard]] Vector to_string_vector() const; bool operator<(Tuple const& other) const { return compare(other) < 0; } bool operator<=(Tuple const& other) const { return compare(other) <= 0; } diff --git a/Userland/Libraries/LibSQL/TupleDescriptor.h b/Userland/Libraries/LibSQL/TupleDescriptor.h index c95dcfcb07..e1744d58ce 100644 --- a/Userland/Libraries/LibSQL/TupleDescriptor.h +++ b/Userland/Libraries/LibSQL/TupleDescriptor.h @@ -7,7 +7,6 @@ #pragma once #include -#include #include namespace SQL { @@ -15,7 +14,7 @@ namespace SQL { struct TupleElement { String name { "" }; SQLType type { SQLType::Text }; - AST::Order order { AST::Order::Ascending }; + Order order { Order::Ascending }; bool operator==(TupleElement const&) const = default; }; diff --git a/Userland/Libraries/LibSQL/Type.h b/Userland/Libraries/LibSQL/Type.h index c9350d899e..865276a1de 100644 --- a/Userland/Libraries/LibSQL/Type.h +++ b/Userland/Libraries/LibSQL/Type.h @@ -37,4 +37,14 @@ inline static size_t size_of(SQLType t) } } +enum class Order { + Ascending, + Descending, +}; + +enum class Nulls { + First, + Last, +}; + }