From 2397836f8e2895d2dc2ecb9685bb383d11145d70 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Thu, 10 Feb 2022 14:43:00 -0500 Subject: [PATCH] LibSQL+SQLServer: Introduce and use ResultOr The result of a SQL statement execution is either: 1. An error. 2. The list of rows inserted, deleted, selected, etc. (2) is currently represented by a combination of the Result class and the ResultSet list it holds. This worked okay, but issues start to arise when trying to use Result in non-statement contexts (for example, when introducing Result to SQL expression execution). What we really need is for Result to be a thin wrapper that represents both (1) and (2), and to not have any explicit members like a ResultSet. So this commit removes ResultSet from Result, and introduces ResultOr, which is just an alias for AK::ErrorOrr. Statement execution now returns ResultOr instead of Result. This further opens the door for expression execution to return ResultOr in the future. Lastly, this moves some other context held by Result over to ResultSet. This includes the row count (which is really just the size of ResultSet) and the command for which the result is for. --- Tests/LibSQL/TestSqlStatementExecution.cpp | 357 +++++++----------- Userland/Libraries/LibSQL/AST/AST.h | 17 +- .../Libraries/LibSQL/AST/CreateSchema.cpp | 8 +- Userland/Libraries/LibSQL/AST/CreateTable.cpp | 12 +- Userland/Libraries/LibSQL/AST/Describe.cpp | 11 +- Userland/Libraries/LibSQL/AST/Insert.cpp | 22 +- Userland/Libraries/LibSQL/AST/Select.cpp | 22 +- Userland/Libraries/LibSQL/AST/Statement.cpp | 2 +- Userland/Libraries/LibSQL/Forward.h | 1 + Userland/Libraries/LibSQL/Result.cpp | 24 -- Userland/Libraries/LibSQL/Result.h | 27 +- Userland/Libraries/LibSQL/ResultSet.cpp | 15 + Userland/Libraries/LibSQL/ResultSet.h | 12 +- Userland/Services/SQLServer/SQLStatement.cpp | 51 ++- Userland/Services/SQLServer/SQLStatement.h | 8 +- 15 files changed, 259 insertions(+), 330 deletions(-) diff --git a/Tests/LibSQL/TestSqlStatementExecution.cpp b/Tests/LibSQL/TestSqlStatementExecution.cpp index 024fcb703c..2551ee34b3 100644 --- a/Tests/LibSQL/TestSqlStatementExecution.cpp +++ b/Tests/LibSQL/TestSqlStatementExecution.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -19,43 +20,46 @@ namespace { constexpr const char* db_name = "/tmp/test.db"; -SQL::Result execute(NonnullRefPtr database, String const& sql) +SQL::ResultOr try_execute(NonnullRefPtr database, String const& sql) { auto parser = SQL::AST::Parser(SQL::AST::Lexer(sql)); auto statement = parser.next_statement(); EXPECT(!parser.has_errors()); if (parser.has_errors()) outln("{}", parser.errors()[0].to_string()); - auto result = statement->execute(move(database)); - if (result.is_error()) - outln("{}", result.error_string()); - return result; + return statement->execute(move(database)); +} + +SQL::ResultSet execute(NonnullRefPtr database, String const& sql) +{ + auto result = try_execute(move(database), sql); + if (result.is_error()) { + outln("{}", result.release_error().error_string()); + VERIFY_NOT_REACHED(); + } + return result.release_value(); } void create_schema(NonnullRefPtr database) { auto result = execute(database, "CREATE SCHEMA TestSchema;"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 1); + EXPECT_EQ(result.command(), SQL::SQLCommand::Create); } void create_table(NonnullRefPtr database) { create_schema(database); auto result = execute(database, "CREATE TABLE TestSchema.TestTable ( TextColumn text, IntColumn integer );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 1); + EXPECT_EQ(result.command(), SQL::SQLCommand::Create); } void create_two_tables(NonnullRefPtr database) { create_schema(database); auto result = execute(database, "CREATE TABLE TestSchema.TestTable1 ( TextColumn1 text, IntColumn integer );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 1); + EXPECT_EQ(result.command(), SQL::SQLCommand::Create); result = execute(database, "CREATE TABLE TestSchema.TestTable2 ( TextColumn2 text, IntColumn integer );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 1); + EXPECT_EQ(result.command(), SQL::SQLCommand::Create); } TEST_CASE(create_schema) @@ -87,8 +91,7 @@ TEST_CASE(insert_into_table) EXPECT(!database->open().is_error()); create_table(database); auto result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test', 42 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 1); + EXPECT(result.size() == 1); auto table_or_error = database->get_table("TESTSCHEMA", "TESTTABLE"); EXPECT(!table_or_error.is_error()); @@ -111,9 +114,9 @@ TEST_CASE(insert_into_table_wrong_data_types) auto database = SQL::Database::construct(db_name); EXPECT(!database->open().is_error()); create_table(database); - auto result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES (43, 'Test_2');"); - EXPECT(result.inserted() == 0); - EXPECT(result.error() == SQL::SQLErrorCode::InvalidValueType); + auto result = try_execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES (43, 'Test_2');"); + EXPECT(result.is_error()); + EXPECT(result.release_error().error() == SQL::SQLErrorCode::InvalidValueType); } TEST_CASE(insert_into_table_multiple_tuples_wrong_data_types) @@ -122,9 +125,9 @@ TEST_CASE(insert_into_table_multiple_tuples_wrong_data_types) auto database = SQL::Database::construct(db_name); EXPECT(!database->open().is_error()); create_table(database); - auto result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ('Test_1', 42), (43, 'Test_2');"); - EXPECT(result.inserted() == 0); - EXPECT(result.error() == SQL::SQLErrorCode::InvalidValueType); + auto result = try_execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ('Test_1', 42), (43, 'Test_2');"); + EXPECT(result.is_error()); + EXPECT(result.release_error().error() == SQL::SQLErrorCode::InvalidValueType); } TEST_CASE(insert_wrong_number_of_values) @@ -133,9 +136,9 @@ TEST_CASE(insert_wrong_number_of_values) auto database = SQL::Database::construct(db_name); EXPECT(!database->open().is_error()); create_table(database); - auto result = execute(database, "INSERT INTO TestSchema.TestTable VALUES ( 42 );"); - EXPECT(result.error() == SQL::SQLErrorCode::InvalidNumberOfValues); - EXPECT(result.inserted() == 0); + auto result = try_execute(database, "INSERT INTO TestSchema.TestTable VALUES ( 42 );"); + EXPECT(result.is_error()); + EXPECT(result.release_error().error() == SQL::SQLErrorCode::InvalidNumberOfValues); } TEST_CASE(insert_identifier_as_value) @@ -144,9 +147,9 @@ TEST_CASE(insert_identifier_as_value) auto database = SQL::Database::construct(db_name); EXPECT(!database->open().is_error()); create_table(database); - auto result = execute(database, "INSERT INTO TestSchema.TestTable VALUES ( identifier, 42 );"); - EXPECT(result.error() == SQL::SQLErrorCode::SyntaxError); - EXPECT(result.inserted() == 0); + auto result = try_execute(database, "INSERT INTO TestSchema.TestTable VALUES ( identifier, 42 );"); + EXPECT(result.is_error()); + EXPECT(result.release_error().error() == SQL::SQLErrorCode::SyntaxError); } TEST_CASE(insert_quoted_identifier_as_value) @@ -155,9 +158,9 @@ TEST_CASE(insert_quoted_identifier_as_value) auto database = SQL::Database::construct(db_name); EXPECT(!database->open().is_error()); create_table(database); - auto result = execute(database, "INSERT INTO TestSchema.TestTable VALUES ( \"QuotedIdentifier\", 42 );"); - EXPECT(result.error() == SQL::SQLErrorCode::SyntaxError); - EXPECT(result.inserted() == 0); + auto result = try_execute(database, "INSERT INTO TestSchema.TestTable VALUES ( \"QuotedIdentifier\", 42 );"); + EXPECT(result.is_error()); + EXPECT(result.release_error().error() == SQL::SQLErrorCode::SyntaxError); } TEST_CASE(insert_without_column_names) @@ -167,8 +170,7 @@ TEST_CASE(insert_without_column_names) EXPECT(!database->open().is_error()); create_table(database); auto result = execute(database, "INSERT INTO TestSchema.TestTable VALUES ('Test_1', 42), ('Test_2', 43);"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 2); + EXPECT(result.size() == 2); auto table_or_error = database->get_table("TESTSCHEMA", "TESTTABLE"); EXPECT(!table_or_error.is_error()); @@ -184,8 +186,7 @@ TEST_CASE(select_from_empty_table) EXPECT(!database->open().is_error()); create_table(database); auto result = execute(database, "SELECT * FROM TestSchema.TestTable;"); - EXPECT(!result.is_error()); - EXPECT(!result.has_results()); + EXPECT(result.is_empty()); } TEST_CASE(select_from_table) @@ -201,12 +202,9 @@ TEST_CASE(select_from_table) "( 'Test_3', 44 ), " "( 'Test_4', 45 ), " "( 'Test_5', 46 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 5); + EXPECT(result.size() == 5); result = execute(database, "SELECT * FROM TestSchema.TestTable;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 5u); + EXPECT_EQ(result.size(), 5u); } TEST_CASE(select_with_column_names) @@ -222,13 +220,10 @@ TEST_CASE(select_with_column_names) "( 'Test_3', 44 ), " "( 'Test_4', 45 ), " "( 'Test_5', 46 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 5); + EXPECT(result.size() == 5); result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 5u); - EXPECT_EQ(result.results()[0].row.size(), 1u); + EXPECT_EQ(result.size(), 5u); + EXPECT_EQ(result[0].row.size(), 1u); } TEST_CASE(select_with_nonexisting_column_name) @@ -244,10 +239,11 @@ TEST_CASE(select_with_nonexisting_column_name) "( 'Test_3', 44 ), " "( 'Test_4', 45 ), " "( 'Test_5', 46 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 5); - result = execute(database, "SELECT Bogus FROM TestSchema.TestTable;"); - EXPECT(result.error() == SQL::SQLErrorCode::ColumnDoesNotExist); + EXPECT(result.size() == 5); + + auto insert_result = try_execute(database, "SELECT Bogus FROM TestSchema.TestTable;"); + EXPECT(insert_result.is_error()); + EXPECT(insert_result.release_error().error() == SQL::SQLErrorCode::ColumnDoesNotExist); } TEST_CASE(select_with_where) @@ -263,13 +259,10 @@ TEST_CASE(select_with_where) "( 'Test_3', 44 ), " "( 'Test_4', 45 ), " "( 'Test_5', 46 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 5); + EXPECT(result.size() == 5); result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable WHERE IntColumn > 44;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 2u); - for (auto& row : result.results()) { + EXPECT_EQ(result.size(), 2u); + for (auto& row : result) { EXPECT(row.row[1].to_int().value() > 44); } } @@ -287,8 +280,7 @@ TEST_CASE(select_cross_join) "( 'Test_3', 44 ), " "( 'Test_4', 45 ), " "( 'Test_5', 46 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 5); + EXPECT(result.size() == 5); result = execute(database, "INSERT INTO TestSchema.TestTable2 ( TextColumn2, IntColumn ) VALUES " "( 'Test_10', 40 ), " @@ -296,13 +288,10 @@ TEST_CASE(select_cross_join) "( 'Test_12', 42 ), " "( 'Test_13', 47 ), " "( 'Test_14', 48 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 5); + EXPECT(result.size() == 5); result = execute(database, "SELECT * FROM TestSchema.TestTable1, TestSchema.TestTable2;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 25u); - for (auto& row : result.results()) { + EXPECT_EQ(result.size(), 25u); + for (auto& row : result) { EXPECT(row.row.size() == 4); EXPECT(row.row[1].to_int().value() >= 42); EXPECT(row.row[1].to_int().value() <= 46); @@ -324,8 +313,7 @@ TEST_CASE(select_inner_join) "( 'Test_3', 44 ), " "( 'Test_4', 45 ), " "( 'Test_5', 46 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 5); + EXPECT(result.size() == 5); result = execute(database, "INSERT INTO TestSchema.TestTable2 ( TextColumn2, IntColumn ) VALUES " "( 'Test_10', 40 ), " @@ -333,20 +321,16 @@ TEST_CASE(select_inner_join) "( 'Test_12', 42 ), " "( 'Test_13', 47 ), " "( 'Test_14', 48 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 5); + EXPECT(result.size() == 5); result = execute(database, "SELECT TestTable1.IntColumn, TextColumn1, TextColumn2 " "FROM TestSchema.TestTable1, TestSchema.TestTable2 " "WHERE TestTable1.IntColumn = TestTable2.IntColumn;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 1u); - auto& row = result.results()[0]; - EXPECT_EQ(row.row.size(), 3u); - EXPECT_EQ(row.row[0].to_int().value(), 42); - EXPECT_EQ(row.row[1].to_string(), "Test_1"); - EXPECT_EQ(row.row[2].to_string(), "Test_12"); + EXPECT_EQ(result.size(), 1u); + EXPECT_EQ(result[0].row.size(), 3u); + EXPECT_EQ(result[0].row[0].to_int().value(), 42); + EXPECT_EQ(result[0].row[1].to_string(), "Test_1"); + EXPECT_EQ(result[0].row[2].to_string(), "Test_12"); } TEST_CASE(select_with_like) @@ -363,63 +347,48 @@ TEST_CASE(select_with_like) "( 'Test+4', 45 ), " "( 'Test+5', 46 ), " "( 'Another+Test_6', 47 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 6); + EXPECT(result.size() == 6); // Simple match result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE 'Test+1';"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 1u); + EXPECT_EQ(result.size(), 1u); // Use % to match most rows result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE 'T%';"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 5u); + EXPECT_EQ(result.size(), 5u); // Same as above but invert the match result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn NOT LIKE 'T%';"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 1u); + EXPECT_EQ(result.size(), 1u); // Use _ and % to match all rows result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE '%e_t%';"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 6u); + EXPECT_EQ(result.size(), 6u); // Use escape to match a single row. The escape character happens to be a // Regex metacharacter, let's make sure we don't get confused by that. result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE '%Test^_%' ESCAPE '^';"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 1u); + EXPECT_EQ(result.size(), 1u); // Same as above but escape the escape character happens to be a SQL // metacharacter - we want to make sure it's treated as an escape. result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE '%Test__%' ESCAPE '_';"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 1u); + EXPECT_EQ(result.size(), 1u); // (Unnecessarily) escaping a character that happens to be a Regex // metacharacter should have no effect. result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE 'Test:+_' ESCAPE ':';"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 5u); + EXPECT_EQ(result.size(), 5u); // Make sure we error out if the ESCAPE is empty - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE '%' ESCAPE '';"); - EXPECT(result.error() == SQL::SQLErrorCode::SyntaxError); - EXPECT(!result.has_results()); + auto select_result = try_execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE '%' ESCAPE '';"); + EXPECT(select_result.is_error()); + EXPECT(select_result.release_error().error() == SQL::SQLErrorCode::SyntaxError); // Make sure we error out if the ESCAPE has more than a single character - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE '%' ESCAPE 'whf';"); - EXPECT(result.error() == SQL::SQLErrorCode::SyntaxError); - EXPECT(!result.has_results()); + select_result = try_execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE '%' ESCAPE 'whf';"); + EXPECT(select_result.is_error()); + EXPECT(select_result.release_error().error() == SQL::SQLErrorCode::SyntaxError); } TEST_CASE(select_with_order) @@ -435,30 +404,23 @@ TEST_CASE(select_with_order) "( 'Test_1', 47 ), " "( 'Test_3', 40 ), " "( 'Test_4', 41 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 5); + EXPECT(result.size() == 5); result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - auto rows = result.results(); - EXPECT_EQ(rows.size(), 5u); - EXPECT_EQ(rows[0].row[1].to_int().value(), 40); - EXPECT_EQ(rows[1].row[1].to_int().value(), 41); - EXPECT_EQ(rows[2].row[1].to_int().value(), 42); - EXPECT_EQ(rows[3].row[1].to_int().value(), 44); - EXPECT_EQ(rows[4].row[1].to_int().value(), 47); + EXPECT_EQ(result.size(), 5u); + EXPECT_EQ(result[0].row[1].to_int().value(), 40); + EXPECT_EQ(result[1].row[1].to_int().value(), 41); + EXPECT_EQ(result[2].row[1].to_int().value(), 42); + EXPECT_EQ(result[3].row[1].to_int().value(), 44); + EXPECT_EQ(result[4].row[1].to_int().value(), 47); result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - rows = result.results(); - EXPECT_EQ(rows.size(), 5u); - EXPECT_EQ(rows[0].row[0].to_string(), "Test_1"); - EXPECT_EQ(rows[1].row[0].to_string(), "Test_2"); - EXPECT_EQ(rows[2].row[0].to_string(), "Test_3"); - EXPECT_EQ(rows[3].row[0].to_string(), "Test_4"); - EXPECT_EQ(rows[4].row[0].to_string(), "Test_5"); + EXPECT_EQ(result.size(), 5u); + EXPECT_EQ(result[0].row[0].to_string(), "Test_1"); + EXPECT_EQ(result[1].row[0].to_string(), "Test_2"); + EXPECT_EQ(result[2].row[0].to_string(), "Test_3"); + EXPECT_EQ(result[3].row[0].to_string(), "Test_4"); + EXPECT_EQ(result[4].row[0].to_string(), "Test_5"); } TEST_CASE(select_with_regexp) @@ -475,34 +437,25 @@ TEST_CASE(select_with_regexp) "( 'Test[4]', 45 ), " "( 'Test+5', 46 ), " "( 'Another-Test_6', 47 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 6); + EXPECT(result.size() == 6); // Simple match result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP 'Test\\+1';"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 1u); + EXPECT_EQ(result.size(), 1u); // Match all result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP '.*';"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 6u); + EXPECT_EQ(result.size(), 6u); // Match with wildcards result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP '^Test.+';"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 4u); + EXPECT_EQ(result.size(), 4u); // Match with case insensitive basic Latin and case sensitive Swedish ö // FIXME: If LibRegex is changed to support case insensitive matches of Unicode characters // This test should be updated and changed to match 'PRÖV'. result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP 'PRöV.*';"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 1u); + EXPECT_EQ(result.size(), 1u); } TEST_CASE(handle_regexp_errors) @@ -514,13 +467,11 @@ TEST_CASE(handle_regexp_errors) auto result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES " "( 'Test', 0 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 1); + EXPECT(result.size() == 1); // Malformed regex, unmatched square bracket - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP 'Test\\+[0-9.*';"); - EXPECT(result.is_error()); - EXPECT(!result.has_results()); + auto select_result = try_execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP 'Test\\+[0-9.*';"); + EXPECT(select_result.is_error()); } TEST_CASE(select_with_order_two_columns) @@ -536,24 +487,20 @@ TEST_CASE(select_with_order_two_columns) "( 'Test_1', 47 ), " "( 'Test_2', 40 ), " "( 'Test_4', 41 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 5); + EXPECT(result.size() == 5); result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn, IntColumn;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - auto rows = result.results(); - EXPECT_EQ(rows.size(), 5u); - EXPECT_EQ(rows[0].row[0].to_string(), "Test_1"); - EXPECT_EQ(rows[0].row[1].to_int().value(), 47); - EXPECT_EQ(rows[1].row[0].to_string(), "Test_2"); - EXPECT_EQ(rows[1].row[1].to_int().value(), 40); - EXPECT_EQ(rows[2].row[0].to_string(), "Test_2"); - EXPECT_EQ(rows[2].row[1].to_int().value(), 42); - EXPECT_EQ(rows[3].row[0].to_string(), "Test_4"); - EXPECT_EQ(rows[3].row[1].to_int().value(), 41); - EXPECT_EQ(rows[4].row[0].to_string(), "Test_5"); - EXPECT_EQ(rows[4].row[1].to_int().value(), 44); + EXPECT_EQ(result.size(), 5u); + EXPECT_EQ(result[0].row[0].to_string(), "Test_1"); + EXPECT_EQ(result[0].row[1].to_int().value(), 47); + EXPECT_EQ(result[1].row[0].to_string(), "Test_2"); + EXPECT_EQ(result[1].row[1].to_int().value(), 40); + EXPECT_EQ(result[2].row[0].to_string(), "Test_2"); + EXPECT_EQ(result[2].row[1].to_int().value(), 42); + EXPECT_EQ(result[3].row[0].to_string(), "Test_4"); + EXPECT_EQ(result[3].row[1].to_int().value(), 41); + EXPECT_EQ(result[4].row[0].to_string(), "Test_5"); + EXPECT_EQ(result[4].row[1].to_int().value(), 44); } TEST_CASE(select_with_order_by_column_not_in_result) @@ -569,19 +516,15 @@ TEST_CASE(select_with_order_by_column_not_in_result) "( 'Test_1', 47 ), " "( 'Test_3', 40 ), " "( 'Test_4', 41 );"); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 5); + EXPECT(result.size() == 5); result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - auto rows = result.results(); - EXPECT_EQ(rows.size(), 5u); - EXPECT_EQ(rows[0].row[0].to_string(), "Test_3"); - EXPECT_EQ(rows[1].row[0].to_string(), "Test_4"); - EXPECT_EQ(rows[2].row[0].to_string(), "Test_2"); - EXPECT_EQ(rows[3].row[0].to_string(), "Test_5"); - EXPECT_EQ(rows[4].row[0].to_string(), "Test_1"); + EXPECT_EQ(result.size(), 5u); + EXPECT_EQ(result[0].row[0].to_string(), "Test_3"); + EXPECT_EQ(result[1].row[0].to_string(), "Test_4"); + EXPECT_EQ(result[2].row[0].to_string(), "Test_2"); + EXPECT_EQ(result[3].row[0].to_string(), "Test_5"); + EXPECT_EQ(result[4].row[0].to_string(), "Test_1"); } TEST_CASE(select_with_limit) @@ -593,13 +536,10 @@ TEST_CASE(select_with_limit) for (auto count = 0; count < 100; count++) { auto result = execute(database, String::formatted("INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_{}', {} );", count, count)); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 1); + EXPECT(result.size() == 1); } auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable LIMIT 10;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - auto rows = result.results(); + auto rows = result; EXPECT_EQ(rows.size(), 10u); } @@ -612,14 +552,10 @@ TEST_CASE(select_with_limit_and_offset) for (auto count = 0; count < 100; count++) { auto result = execute(database, String::formatted("INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_{}', {} );", count, count)); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 1); + EXPECT(result.size() == 1); } auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable LIMIT 10 OFFSET 10;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - auto rows = result.results(); - EXPECT_EQ(rows.size(), 10u); + EXPECT_EQ(result.size(), 10u); } TEST_CASE(select_with_order_limit_and_offset) @@ -631,24 +567,20 @@ TEST_CASE(select_with_order_limit_and_offset) for (auto count = 0; count < 100; count++) { auto result = execute(database, String::formatted("INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_{}', {} );", count, count)); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 1); + EXPECT(result.size() == 1); } auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY IntColumn LIMIT 10 OFFSET 10;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - auto rows = result.results(); - EXPECT_EQ(rows.size(), 10u); - EXPECT_EQ(rows[0].row[1].to_int().value(), 10); - EXPECT_EQ(rows[1].row[1].to_int().value(), 11); - EXPECT_EQ(rows[2].row[1].to_int().value(), 12); - EXPECT_EQ(rows[3].row[1].to_int().value(), 13); - EXPECT_EQ(rows[4].row[1].to_int().value(), 14); - EXPECT_EQ(rows[5].row[1].to_int().value(), 15); - EXPECT_EQ(rows[6].row[1].to_int().value(), 16); - EXPECT_EQ(rows[7].row[1].to_int().value(), 17); - EXPECT_EQ(rows[8].row[1].to_int().value(), 18); - EXPECT_EQ(rows[9].row[1].to_int().value(), 19); + EXPECT_EQ(result.size(), 10u); + EXPECT_EQ(result[0].row[1].to_int().value(), 10); + EXPECT_EQ(result[1].row[1].to_int().value(), 11); + EXPECT_EQ(result[2].row[1].to_int().value(), 12); + EXPECT_EQ(result[3].row[1].to_int().value(), 13); + EXPECT_EQ(result[4].row[1].to_int().value(), 14); + EXPECT_EQ(result[5].row[1].to_int().value(), 15); + EXPECT_EQ(result[6].row[1].to_int().value(), 16); + EXPECT_EQ(result[7].row[1].to_int().value(), 17); + EXPECT_EQ(result[8].row[1].to_int().value(), 18); + EXPECT_EQ(result[9].row[1].to_int().value(), 19); } TEST_CASE(select_with_limit_out_of_bounds) @@ -660,14 +592,10 @@ TEST_CASE(select_with_limit_out_of_bounds) for (auto count = 0; count < 100; count++) { auto result = execute(database, String::formatted("INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_{}', {} );", count, count)); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 1); + EXPECT(result.size() == 1); } auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable LIMIT 500;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - auto rows = result.results(); - EXPECT_EQ(rows.size(), 100u); + EXPECT_EQ(result.size(), 100u); } TEST_CASE(select_with_offset_out_of_bounds) @@ -679,14 +607,10 @@ TEST_CASE(select_with_offset_out_of_bounds) for (auto count = 0; count < 100; count++) { auto result = execute(database, String::formatted("INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_{}', {} );", count, count)); - EXPECT(!result.is_error()); - EXPECT(result.inserted() == 1); + EXPECT(result.size() == 1); } auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable LIMIT 10 OFFSET 200;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - auto rows = result.results(); - EXPECT_EQ(rows.size(), 0u); + EXPECT_EQ(result.size(), 0u); } TEST_CASE(describe_table) @@ -696,18 +620,13 @@ TEST_CASE(describe_table) EXPECT(!database->open().is_error()); create_table(database); auto result = execute(database, "DESCRIBE TABLE TestSchema.TestTable;"); - EXPECT(!result.is_error()); - EXPECT(result.has_results()); - EXPECT_EQ(result.results().size(), 2u); + EXPECT_EQ(result.size(), 2u); - auto rows = result.results(); - auto& row1 = rows[0]; - EXPECT_EQ(row1.row[0].to_string(), "TEXTCOLUMN"); - EXPECT_EQ(row1.row[1].to_string(), "text"); + EXPECT_EQ(result[0].row[0].to_string(), "TEXTCOLUMN"); + EXPECT_EQ(result[0].row[1].to_string(), "text"); - auto& row2 = rows[1]; - EXPECT_EQ(row2.row[0].to_string(), "INTCOLUMN"); - EXPECT_EQ(row2.row[1].to_string(), "int"); + EXPECT_EQ(result[1].row[0].to_string(), "INTCOLUMN"); + EXPECT_EQ(result[1].row[1].to_string(), "int"); } } diff --git a/Userland/Libraries/LibSQL/AST/AST.h b/Userland/Libraries/LibSQL/AST/AST.h index a1d67f53da..50017b580d 100644 --- a/Userland/Libraries/LibSQL/AST/AST.h +++ b/Userland/Libraries/LibSQL/AST/AST.h @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace SQL::AST { @@ -725,11 +726,11 @@ private: class Statement : public ASTNode { public: - Result execute(AK::NonnullRefPtr database) const; + ResultOr execute(AK::NonnullRefPtr database) const; - virtual Result execute(ExecutionContext&) const + virtual ResultOr execute(ExecutionContext&) const { - return { SQLCommand::Unknown, SQLErrorCode::NotYetImplemented }; + return Result { SQLCommand::Unknown, SQLErrorCode::NotYetImplemented }; } }; @@ -747,7 +748,7 @@ public: const String& schema_name() const { return m_schema_name; } bool is_error_if_schema_exists() const { return m_is_error_if_schema_exists; } - Result execute(ExecutionContext&) const override; + ResultOr execute(ExecutionContext&) const override; private: String m_schema_name; @@ -786,7 +787,7 @@ public: bool is_temporary() const { return m_is_temporary; } bool is_error_if_table_exists() const { return m_is_error_if_table_exists; } - Result execute(ExecutionContext&) const override; + ResultOr execute(ExecutionContext&) const override; private: String m_schema_name; @@ -949,7 +950,7 @@ public: bool has_selection() const { return !m_select_statement.is_null(); } const RefPtr