mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 11:12:45 +00:00 
			
		
		
		
	LibSQL+SQLServer: Return the new Result class from statement executions
We can now TRY anything that returns a SQL::Result or an AK::Error.
This commit is contained in:
		
							parent
							
								
									d9055de7ea
								
							
						
					
					
						commit
						6620f19979
					
				
					 11 changed files with 337 additions and 337 deletions
				
			
		|  | @ -19,7 +19,7 @@ namespace { | |||
| 
 | ||||
| constexpr const char* db_name = "/tmp/test.db"; | ||||
| 
 | ||||
| RefPtr<SQL::SQLResult> execute(NonnullRefPtr<SQL::Database> database, String const& sql) | ||||
| SQL::Result execute(NonnullRefPtr<SQL::Database> database, String const& sql) | ||||
| { | ||||
|     auto parser = SQL::AST::Parser(SQL::AST::Lexer(sql)); | ||||
|     auto statement = parser.next_statement(); | ||||
|  | @ -27,35 +27,35 @@ RefPtr<SQL::SQLResult> execute(NonnullRefPtr<SQL::Database> database, String con | |||
|     if (parser.has_errors()) | ||||
|         outln("{}", parser.errors()[0].to_string()); | ||||
|     auto result = statement->execute(move(database)); | ||||
|     if (result->error().code != SQL::SQLErrorCode::NoError) | ||||
|         outln("{}", result->error().to_string()); | ||||
|     if (result.is_error()) | ||||
|         outln("{}", result.error_string()); | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| void create_schema(NonnullRefPtr<SQL::Database> database) | ||||
| { | ||||
|     auto result = execute(database, "CREATE SCHEMA TestSchema;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 1); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 1); | ||||
| } | ||||
| 
 | ||||
| void create_table(NonnullRefPtr<SQL::Database> database) | ||||
| { | ||||
|     create_schema(database); | ||||
|     auto result = execute(database, "CREATE TABLE TestSchema.TestTable ( TextColumn text, IntColumn integer );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 1); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 1); | ||||
| } | ||||
| 
 | ||||
| void create_two_tables(NonnullRefPtr<SQL::Database> database) | ||||
| { | ||||
|     create_schema(database); | ||||
|     auto result = execute(database, "CREATE TABLE TestSchema.TestTable1 ( TextColumn1 text, IntColumn integer );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 1); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 1); | ||||
|     result = execute(database, "CREATE TABLE TestSchema.TestTable2 ( TextColumn2 text, IntColumn integer );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 1); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 1); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(create_schema) | ||||
|  | @ -87,8 +87,8 @@ 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->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 1); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 1); | ||||
| 
 | ||||
|     auto table_or_error = database->get_table("TESTSCHEMA", "TESTTABLE"); | ||||
|     EXPECT(!table_or_error.is_error()); | ||||
|  | @ -112,8 +112,8 @@ TEST_CASE(insert_into_table_wrong_data_types) | |||
|     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().code == SQL::SQLErrorCode::InvalidValueType); | ||||
|     EXPECT(result.inserted() == 0); | ||||
|     EXPECT(result.error() == SQL::SQLErrorCode::InvalidValueType); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(insert_into_table_multiple_tuples_wrong_data_types) | ||||
|  | @ -123,8 +123,8 @@ TEST_CASE(insert_into_table_multiple_tuples_wrong_data_types) | |||
|     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().code == SQL::SQLErrorCode::InvalidValueType); | ||||
|     EXPECT(result.inserted() == 0); | ||||
|     EXPECT(result.error() == SQL::SQLErrorCode::InvalidValueType); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(insert_wrong_number_of_values) | ||||
|  | @ -134,8 +134,8 @@ TEST_CASE(insert_wrong_number_of_values) | |||
|     EXPECT(!database->open().is_error()); | ||||
|     create_table(database); | ||||
|     auto result = execute(database, "INSERT INTO TestSchema.TestTable VALUES ( 42 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::InvalidNumberOfValues); | ||||
|     EXPECT(result->inserted() == 0); | ||||
|     EXPECT(result.error() == SQL::SQLErrorCode::InvalidNumberOfValues); | ||||
|     EXPECT(result.inserted() == 0); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(insert_identifier_as_value) | ||||
|  | @ -145,8 +145,8 @@ TEST_CASE(insert_identifier_as_value) | |||
|     EXPECT(!database->open().is_error()); | ||||
|     create_table(database); | ||||
|     auto result = execute(database, "INSERT INTO TestSchema.TestTable VALUES ( identifier, 42 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::SyntaxError); | ||||
|     EXPECT(result->inserted() == 0); | ||||
|     EXPECT(result.error() == SQL::SQLErrorCode::SyntaxError); | ||||
|     EXPECT(result.inserted() == 0); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(insert_quoted_identifier_as_value) | ||||
|  | @ -156,8 +156,8 @@ TEST_CASE(insert_quoted_identifier_as_value) | |||
|     EXPECT(!database->open().is_error()); | ||||
|     create_table(database); | ||||
|     auto result = execute(database, "INSERT INTO TestSchema.TestTable VALUES ( \"QuotedIdentifier\", 42 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::SyntaxError); | ||||
|     EXPECT(result->inserted() == 0); | ||||
|     EXPECT(result.error() == SQL::SQLErrorCode::SyntaxError); | ||||
|     EXPECT(result.inserted() == 0); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(insert_without_column_names) | ||||
|  | @ -167,8 +167,8 @@ 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->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 2); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 2); | ||||
| 
 | ||||
|     auto table_or_error = database->get_table("TESTSCHEMA", "TESTTABLE"); | ||||
|     EXPECT(!table_or_error.is_error()); | ||||
|  | @ -190,12 +190,12 @@ TEST_CASE(select_from_table) | |||
|         "( 'Test_3', 44 ), " | ||||
|         "( 'Test_4', 45 ), " | ||||
|         "( 'Test_5', 46 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 5); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 5); | ||||
|     result = execute(database, "SELECT * FROM TestSchema.TestTable;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 5u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().size(), 5u); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(select_with_column_names) | ||||
|  | @ -211,13 +211,13 @@ TEST_CASE(select_with_column_names) | |||
|         "( 'Test_3', 44 ), " | ||||
|         "( 'Test_4', 45 ), " | ||||
|         "( 'Test_5', 46 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 5); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 5); | ||||
|     result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 5u); | ||||
|     EXPECT_EQ(result->results()[0].row.size(), 1u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().size(), 5u); | ||||
|     EXPECT_EQ(result.results()[0].row.size(), 1u); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(select_with_nonexisting_column_name) | ||||
|  | @ -233,10 +233,10 @@ TEST_CASE(select_with_nonexisting_column_name) | |||
|         "( 'Test_3', 44 ), " | ||||
|         "( 'Test_4', 45 ), " | ||||
|         "( 'Test_5', 46 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 5); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 5); | ||||
|     result = execute(database, "SELECT Bogus FROM TestSchema.TestTable;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::ColumnDoesNotExist); | ||||
|     EXPECT(result.error() == SQL::SQLErrorCode::ColumnDoesNotExist); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(select_with_where) | ||||
|  | @ -252,13 +252,13 @@ TEST_CASE(select_with_where) | |||
|         "( 'Test_3', 44 ), " | ||||
|         "( 'Test_4', 45 ), " | ||||
|         "( 'Test_5', 46 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 5); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 5); | ||||
|     result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable WHERE IntColumn > 44;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 2u); | ||||
|     for (auto& row : result->results()) { | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().size(), 2u); | ||||
|     for (auto& row : result.results()) { | ||||
|         EXPECT(row.row[1].to_int().value() > 44); | ||||
|     } | ||||
| } | ||||
|  | @ -276,8 +276,8 @@ TEST_CASE(select_cross_join) | |||
|         "( 'Test_3', 44 ), " | ||||
|         "( 'Test_4', 45 ), " | ||||
|         "( 'Test_5', 46 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 5); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 5); | ||||
|     result = execute(database, | ||||
|         "INSERT INTO TestSchema.TestTable2 ( TextColumn2, IntColumn ) VALUES " | ||||
|         "( 'Test_10', 40 ), " | ||||
|  | @ -285,13 +285,13 @@ TEST_CASE(select_cross_join) | |||
|         "( 'Test_12', 42 ), " | ||||
|         "( 'Test_13', 47 ), " | ||||
|         "( 'Test_14', 48 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 5); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 5); | ||||
|     result = execute(database, "SELECT * FROM TestSchema.TestTable1, TestSchema.TestTable2;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 25u); | ||||
|     for (auto& row : result->results()) { | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().size(), 25u); | ||||
|     for (auto& row : result.results()) { | ||||
|         EXPECT(row.row.size() == 4); | ||||
|         EXPECT(row.row[1].to_int().value() >= 42); | ||||
|         EXPECT(row.row[1].to_int().value() <= 46); | ||||
|  | @ -313,8 +313,8 @@ TEST_CASE(select_inner_join) | |||
|         "( 'Test_3', 44 ), " | ||||
|         "( 'Test_4', 45 ), " | ||||
|         "( 'Test_5', 46 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 5); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 5); | ||||
|     result = execute(database, | ||||
|         "INSERT INTO TestSchema.TestTable2 ( TextColumn2, IntColumn ) VALUES " | ||||
|         "( 'Test_10', 40 ), " | ||||
|  | @ -322,16 +322,16 @@ TEST_CASE(select_inner_join) | |||
|         "( 'Test_12', 42 ), " | ||||
|         "( 'Test_13', 47 ), " | ||||
|         "( 'Test_14', 48 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 5); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 5); | ||||
|     result = execute(database, | ||||
|         "SELECT TestTable1.IntColumn, TextColumn1, TextColumn2 " | ||||
|         "FROM TestSchema.TestTable1, TestSchema.TestTable2 " | ||||
|         "WHERE TestTable1.IntColumn = TestTable2.IntColumn;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 1u); | ||||
|     auto& row = result->results()[0]; | ||||
|     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"); | ||||
|  | @ -352,63 +352,63 @@ TEST_CASE(select_with_like) | |||
|         "( 'Test+4', 45 ), " | ||||
|         "( 'Test+5', 46 ), " | ||||
|         "( 'Another+Test_6', 47 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 6); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 6); | ||||
| 
 | ||||
|     // Simple match
 | ||||
|     result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE 'Test+1';"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 1u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().size(), 1u); | ||||
| 
 | ||||
|     // Use % to match most rows
 | ||||
|     result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE 'T%';"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 5u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().size(), 5u); | ||||
| 
 | ||||
|     // Same as above but invert the match
 | ||||
|     result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn NOT LIKE 'T%';"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 1u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().size(), 1u); | ||||
| 
 | ||||
|     // Use _ and % to match all rows
 | ||||
|     result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE '%e_t%';"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 6u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().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->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 1u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().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->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 1u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().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->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 5u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().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().code == SQL::SQLErrorCode::SyntaxError); | ||||
|     EXPECT(!result->has_results()); | ||||
|     EXPECT(result.error() == SQL::SQLErrorCode::SyntaxError); | ||||
|     EXPECT(!result.has_results()); | ||||
| 
 | ||||
|     // 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().code == SQL::SQLErrorCode::SyntaxError); | ||||
|     EXPECT(!result->has_results()); | ||||
|     EXPECT(result.error() == SQL::SQLErrorCode::SyntaxError); | ||||
|     EXPECT(!result.has_results()); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(select_with_order) | ||||
|  | @ -424,13 +424,13 @@ TEST_CASE(select_with_order) | |||
|         "( 'Test_1', 47 ), " | ||||
|         "( 'Test_3', 40 ), " | ||||
|         "( 'Test_4', 41 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 5); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 5); | ||||
| 
 | ||||
|     result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     auto rows = result->results(); | ||||
|     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); | ||||
|  | @ -439,9 +439,9 @@ TEST_CASE(select_with_order) | |||
|     EXPECT_EQ(rows[4].row[1].to_int().value(), 47); | ||||
| 
 | ||||
|     result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     rows = result->results(); | ||||
|     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"); | ||||
|  | @ -464,34 +464,34 @@ TEST_CASE(select_with_regexp) | |||
|         "( 'Test[4]', 45 ), " | ||||
|         "( 'Test+5', 46 ), " | ||||
|         "( 'Another-Test_6', 47 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 6); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 6); | ||||
| 
 | ||||
|     // Simple match
 | ||||
|     result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP 'Test\\+1';"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 1u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().size(), 1u); | ||||
| 
 | ||||
|     // Match all
 | ||||
|     result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP '.*';"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 6u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().size(), 6u); | ||||
| 
 | ||||
|     // Match with wildcards
 | ||||
|     result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP '^Test.+';"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 4u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().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->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 1u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().size(), 1u); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(handle_regexp_errors) | ||||
|  | @ -503,13 +503,13 @@ TEST_CASE(handle_regexp_errors) | |||
|     auto result = execute(database, | ||||
|         "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES " | ||||
|         "( 'Test', 0 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 1); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 1); | ||||
| 
 | ||||
|     // Malformed regex, unmatched square bracket
 | ||||
|     result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP 'Test\\+[0-9.*';"); | ||||
|     EXPECT(result->error().code != SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(!result->has_results()); | ||||
|     EXPECT(result.is_error()); | ||||
|     EXPECT(!result.has_results()); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(select_with_order_two_columns) | ||||
|  | @ -525,13 +525,13 @@ TEST_CASE(select_with_order_two_columns) | |||
|         "( 'Test_1', 47 ), " | ||||
|         "( 'Test_2', 40 ), " | ||||
|         "( 'Test_4', 41 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 5); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 5); | ||||
| 
 | ||||
|     result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn, IntColumn;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     auto rows = result->results(); | ||||
|     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); | ||||
|  | @ -558,13 +558,13 @@ TEST_CASE(select_with_order_by_column_not_in_result) | |||
|         "( 'Test_1', 47 ), " | ||||
|         "( 'Test_3', 40 ), " | ||||
|         "( 'Test_4', 41 );"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->inserted() == 5); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.inserted() == 5); | ||||
| 
 | ||||
|     result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     auto rows = result->results(); | ||||
|     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"); | ||||
|  | @ -582,13 +582,13 @@ 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->error().code == SQL::SQLErrorCode::NoError); | ||||
|         EXPECT(result->inserted() == 1); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT(result.inserted() == 1); | ||||
|     } | ||||
|     auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable LIMIT 10;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     auto rows = result->results(); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     auto rows = result.results(); | ||||
|     EXPECT_EQ(rows.size(), 10u); | ||||
| } | ||||
| 
 | ||||
|  | @ -601,13 +601,13 @@ 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->error().code == SQL::SQLErrorCode::NoError); | ||||
|         EXPECT(result->inserted() == 1); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT(result.inserted() == 1); | ||||
|     } | ||||
|     auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable LIMIT 10 OFFSET 10;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     auto rows = result->results(); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     auto rows = result.results(); | ||||
|     EXPECT_EQ(rows.size(), 10u); | ||||
| } | ||||
| 
 | ||||
|  | @ -620,13 +620,13 @@ 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->error().code == SQL::SQLErrorCode::NoError); | ||||
|         EXPECT(result->inserted() == 1); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT(result.inserted() == 1); | ||||
|     } | ||||
|     auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY IntColumn LIMIT 10 OFFSET 10;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     auto rows = result->results(); | ||||
|     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); | ||||
|  | @ -649,13 +649,13 @@ 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->error().code == SQL::SQLErrorCode::NoError); | ||||
|         EXPECT(result->inserted() == 1); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT(result.inserted() == 1); | ||||
|     } | ||||
|     auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable LIMIT 500;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     auto rows = result->results(); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     auto rows = result.results(); | ||||
|     EXPECT_EQ(rows.size(), 100u); | ||||
| } | ||||
| 
 | ||||
|  | @ -668,13 +668,13 @@ 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->error().code == SQL::SQLErrorCode::NoError); | ||||
|         EXPECT(result->inserted() == 1); | ||||
|         EXPECT(!result.is_error()); | ||||
|         EXPECT(result.inserted() == 1); | ||||
|     } | ||||
|     auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable LIMIT 10 OFFSET 200;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     auto rows = result->results(); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     auto rows = result.results(); | ||||
|     EXPECT_EQ(rows.size(), 0u); | ||||
| } | ||||
| 
 | ||||
|  | @ -685,11 +685,11 @@ TEST_CASE(describe_table) | |||
|     EXPECT(!database->open().is_error()); | ||||
|     create_table(database); | ||||
|     auto result = execute(database, "DESCRIBE TABLE TestSchema.TestTable;"); | ||||
|     EXPECT(result->error().code == SQL::SQLErrorCode::NoError); | ||||
|     EXPECT(result->has_results()); | ||||
|     EXPECT_EQ(result->results().size(), 2u); | ||||
|     EXPECT(!result.is_error()); | ||||
|     EXPECT(result.has_results()); | ||||
|     EXPECT_EQ(result.results().size(), 2u); | ||||
| 
 | ||||
|     auto rows = result->results(); | ||||
|     auto rows = result.results(); | ||||
|     auto& row1 = rows[0]; | ||||
|     EXPECT_EQ(row1.row[0].to_string(), "TEXTCOLUMN"); | ||||
|     EXPECT_EQ(row1.row[1].to_string(), "text"); | ||||
|  |  | |||
|  | @ -298,7 +298,7 @@ private: | |||
| 
 | ||||
| struct ExecutionContext { | ||||
|     NonnullRefPtr<Database> database; | ||||
|     RefPtr<SQLResult> result { nullptr }; | ||||
|     Optional<Result> result; | ||||
|     class Statement const* statement; | ||||
|     Tuple* current_row { nullptr }; | ||||
| }; | ||||
|  | @ -725,8 +725,12 @@ private: | |||
| 
 | ||||
| class Statement : public ASTNode { | ||||
| public: | ||||
|     RefPtr<SQLResult> execute(AK::NonnullRefPtr<Database> database) const; | ||||
|     virtual RefPtr<SQLResult> execute(ExecutionContext&) const { return nullptr; } | ||||
|     Result execute(AK::NonnullRefPtr<Database> database) const; | ||||
| 
 | ||||
|     virtual Result execute(ExecutionContext&) const | ||||
|     { | ||||
|         return { SQLCommand::Unknown, SQLErrorCode::NotYetImplemented }; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| class ErrorStatement final : public Statement { | ||||
|  | @ -743,7 +747,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; } | ||||
| 
 | ||||
|     RefPtr<SQLResult> execute(ExecutionContext&) const override; | ||||
|     Result execute(ExecutionContext&) const override; | ||||
| 
 | ||||
| private: | ||||
|     String m_schema_name; | ||||
|  | @ -782,7 +786,7 @@ 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(ExecutionContext&) const override; | ||||
|     Result execute(ExecutionContext&) const override; | ||||
| 
 | ||||
| private: | ||||
|     String m_schema_name; | ||||
|  | @ -945,7 +949,7 @@ public: | |||
|     bool has_selection() const { return !m_select_statement.is_null(); } | ||||
|     const RefPtr<Select>& select_statement() const { return m_select_statement; } | ||||
| 
 | ||||
|     virtual RefPtr<SQLResult> execute(ExecutionContext&) const override; | ||||
|     virtual Result execute(ExecutionContext&) const override; | ||||
| 
 | ||||
| private: | ||||
|     RefPtr<CommonTableExpressionList> m_common_table_expression_list; | ||||
|  | @ -1038,7 +1042,7 @@ public: | |||
|     const RefPtr<GroupByClause>& group_by_clause() const { return m_group_by_clause; } | ||||
|     const NonnullRefPtrVector<OrderingTerm>& ordering_term_list() const { return m_ordering_term_list; } | ||||
|     const RefPtr<LimitClause>& limit_clause() const { return m_limit_clause; } | ||||
|     RefPtr<SQLResult> execute(ExecutionContext&) const override; | ||||
|     Result execute(ExecutionContext&) const override; | ||||
| 
 | ||||
| private: | ||||
|     RefPtr<CommonTableExpressionList> m_common_table_expression_list; | ||||
|  | @ -1059,7 +1063,7 @@ public: | |||
|     } | ||||
| 
 | ||||
|     NonnullRefPtr<QualifiedTableName> qualified_table_name() const { return m_qualified_table_name; } | ||||
|     RefPtr<SQLResult> execute(ExecutionContext&) const override; | ||||
|     Result execute(ExecutionContext&) const override; | ||||
| 
 | ||||
| private: | ||||
|     NonnullRefPtr<QualifiedTableName> m_qualified_table_name; | ||||
|  |  | |||
|  | @ -10,23 +10,20 @@ | |||
| 
 | ||||
| namespace SQL::AST { | ||||
| 
 | ||||
| RefPtr<SQLResult> CreateSchema::execute(ExecutionContext& context) const | ||||
| Result CreateSchema::execute(ExecutionContext& context) const | ||||
| { | ||||
|     auto schema_def_or_error = context.database->get_schema(m_schema_name); | ||||
|     if (schema_def_or_error.is_error()) | ||||
|         return SQLResult::construct(SQLCommand::Create, SQLErrorCode::InternalError, schema_def_or_error.error()); | ||||
|     auto schema_def = schema_def_or_error.release_value(); | ||||
|     auto schema_def = TRY(context.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); | ||||
|         if (m_is_error_if_schema_exists) | ||||
|             return { SQLCommand::Create, SQLErrorCode::SchemaExists, m_schema_name }; | ||||
|         return { SQLCommand::Create }; | ||||
|     } | ||||
| 
 | ||||
|     schema_def = SchemaDef::construct(m_schema_name); | ||||
|     if (auto maybe_error = context.database->add_schema(*schema_def); maybe_error.is_error()) | ||||
|         return SQLResult::construct(SQLCommand::Create, SQLErrorCode::InternalError, maybe_error.error()); | ||||
|     return SQLResult::construct(SQLCommand::Create, 0, 1); | ||||
|     TRY(context.database->add_schema(*schema_def)); | ||||
| 
 | ||||
|     return { SQLCommand::Create, 0, 1 }; | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -9,43 +9,40 @@ | |||
| 
 | ||||
| namespace SQL::AST { | ||||
| 
 | ||||
| RefPtr<SQLResult> CreateTable::execute(ExecutionContext& context) const | ||||
| Result CreateTable::execute(ExecutionContext& context) const | ||||
| { | ||||
|     auto schema_name = (!m_schema_name.is_null() && !m_schema_name.is_empty()) ? m_schema_name : "default"; | ||||
|     auto schema_def_or_error = context.database->get_schema(schema_name); | ||||
|     if (schema_def_or_error.is_error()) | ||||
|         return SQLResult::construct(SQLCommand::Create, SQLErrorCode::InternalError, schema_def_or_error.error()); | ||||
|     auto schema_def = schema_def_or_error.release_value(); | ||||
|     auto schema_name = m_schema_name.is_empty() ? String { "default"sv } : m_schema_name; | ||||
| 
 | ||||
|     auto schema_def = TRY(context.database->get_schema(schema_name)); | ||||
|     if (!schema_def) | ||||
|         return SQLResult::construct(SQLCommand::Create, SQLErrorCode::SchemaDoesNotExist, m_schema_name); | ||||
|     auto table_def_or_error = context.database->get_table(schema_name, m_table_name); | ||||
|     if (table_def_or_error.is_error()) | ||||
|         return SQLResult::construct(SQLCommand::Create, SQLErrorCode::InternalError, table_def_or_error.error()); | ||||
|     auto table_def = table_def_or_error.release_value(); | ||||
|         return { SQLCommand::Create, SQLErrorCode::SchemaDoesNotExist, schema_name }; | ||||
| 
 | ||||
|     auto table_def = TRY(context.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); | ||||
|         } | ||||
|         if (m_is_error_if_table_exists) | ||||
|             return { SQLCommand::Create, SQLErrorCode::TableExists, m_table_name }; | ||||
|         return { 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") { | ||||
| 
 | ||||
|         if (column.type_name()->name().is_one_of("VARCHAR"sv, "TEXT"sv)) | ||||
|             type = SQLType::Text; | ||||
|         } else if (column.type_name()->name() == "INT" || column.type_name()->name() == "INTEGER") { | ||||
|         else if (column.type_name()->name().is_one_of("INT"sv, "INTEGER"sv)) | ||||
|             type = SQLType::Integer; | ||||
|         } else if (column.type_name()->name() == "FLOAT" || column.type_name()->name() == "NUMBER") { | ||||
|         else if (column.type_name()->name().is_one_of("FLOAT"sv, "NUMBER"sv)) | ||||
|             type = SQLType::Float; | ||||
|         } else { | ||||
|             return SQLResult::construct(SQLCommand::Create, SQLErrorCode::InvalidType, column.type_name()->name()); | ||||
|         } | ||||
|         else | ||||
|             return { SQLCommand::Create, SQLErrorCode::InvalidType, column.type_name()->name() }; | ||||
| 
 | ||||
|         table_def->append_column(column.name(), type); | ||||
|     } | ||||
|     if (auto maybe_error = context.database->add_table(*table_def); maybe_error.is_error()) | ||||
|         return SQLResult::construct(SQLCommand::Create, SQLErrorCode::InternalError, maybe_error.release_error()); | ||||
|     return SQLResult::construct(SQLCommand::Create, 0, 1); | ||||
| 
 | ||||
|     TRY(context.database->add_table(*table_def)); | ||||
|     return { SQLCommand::Create, 0, 1 }; | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -11,32 +11,31 @@ | |||
| 
 | ||||
| namespace SQL::AST { | ||||
| 
 | ||||
| RefPtr<SQLResult> DescribeTable::execute(ExecutionContext& context) const | ||||
| Result DescribeTable::execute(ExecutionContext& context) const | ||||
| { | ||||
|     auto schema_name = m_qualified_table_name->schema_name(); | ||||
|     auto table_name = m_qualified_table_name->table_name(); | ||||
| 
 | ||||
|     auto table_def_or_error = context.database->get_table(schema_name, table_name); | ||||
|     auto table_def = table_def_or_error.release_value(); | ||||
|     auto table_def = TRY(context.database->get_table(schema_name, table_name)); | ||||
|     if (!table_def) { | ||||
|         if (schema_name.is_null() || schema_name.is_empty()) | ||||
|             schema_name = "default"; | ||||
|         return SQLResult::construct(SQLCommand::Describe, SQLErrorCode::TableDoesNotExist, String::formatted("{}.{}", schema_name, table_name)); | ||||
|         if (schema_name.is_empty()) | ||||
|             schema_name = "default"sv; | ||||
|         return { SQLCommand::Describe, SQLErrorCode::TableDoesNotExist, String::formatted("{}.{}", schema_name, table_name) }; | ||||
|     } | ||||
| 
 | ||||
|     auto describe_table_def = context.database->get_table("master", "internal_describe_table").value(); | ||||
|     NonnullRefPtr<TupleDescriptor> descriptor = describe_table_def->to_tuple_descriptor(); | ||||
|     auto describe_table_def = MUST(context.database->get_table("master"sv, "internal_describe_table"sv)); | ||||
|     auto descriptor = describe_table_def->to_tuple_descriptor(); | ||||
| 
 | ||||
|     context.result = SQLResult::construct(); | ||||
|     Result result { SQLCommand::Describe }; | ||||
| 
 | ||||
|     for (auto& column : table_def->columns()) { | ||||
|         Tuple tuple(descriptor); | ||||
|         tuple[0] = column.name(); | ||||
|         tuple[1] = SQLType_name(column.type()); | ||||
|         context.result->insert(tuple, Tuple {}); | ||||
|         result.insert(tuple, Tuple {}); | ||||
|     } | ||||
| 
 | ||||
|     return context.result; | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ Value Expression::evaluate(ExecutionContext&) const | |||
| 
 | ||||
| Value NumericLiteral::evaluate(ExecutionContext& context) const | ||||
| { | ||||
|     if (context.result->has_error()) | ||||
|     if (context.result->is_error()) | ||||
|         return Value::null(); | ||||
|     Value ret(SQLType::Float); | ||||
|     ret = value(); | ||||
|  | @ -28,7 +28,7 @@ Value NumericLiteral::evaluate(ExecutionContext& context) const | |||
| 
 | ||||
| Value StringLiteral::evaluate(ExecutionContext& context) const | ||||
| { | ||||
|     if (context.result->has_error()) | ||||
|     if (context.result->is_error()) | ||||
|         return Value::null(); | ||||
|     Value ret(SQLType::Text); | ||||
|     ret = value(); | ||||
|  | @ -42,14 +42,14 @@ Value NullLiteral::evaluate(ExecutionContext&) const | |||
| 
 | ||||
| Value NestedExpression::evaluate(ExecutionContext& context) const | ||||
| { | ||||
|     if (context.result->has_error()) | ||||
|     if (context.result->is_error()) | ||||
|         return Value::null(); | ||||
|     return expression()->evaluate(context); | ||||
| } | ||||
| 
 | ||||
| Value ChainedExpression::evaluate(ExecutionContext& context) const | ||||
| { | ||||
|     if (context.result->has_error()) | ||||
|     if (context.result->is_error()) | ||||
|         return Value::null(); | ||||
|     Value ret(SQLType::Tuple); | ||||
|     Vector<Value> values; | ||||
|  | @ -62,14 +62,14 @@ Value ChainedExpression::evaluate(ExecutionContext& context) const | |||
| 
 | ||||
| Value BinaryOperatorExpression::evaluate(ExecutionContext& context) const | ||||
| { | ||||
|     if (context.result->has_error()) | ||||
|     if (context.result->is_error()) | ||||
|         return Value::null(); | ||||
|     Value lhs_value = lhs()->evaluate(context); | ||||
|     Value rhs_value = rhs()->evaluate(context); | ||||
|     switch (type()) { | ||||
|     case BinaryOperator::Concatenate: { | ||||
|         if (lhs_value.type() != SQLType::Text) { | ||||
|             context.result->set_error(SQLErrorCode::BooleanOperatorTypeMismatch, BinaryOperator_name(type())); | ||||
|             context.result = Result { SQLCommand::Unknown, SQLErrorCode::BooleanOperatorTypeMismatch, BinaryOperator_name(type()) }; | ||||
|             return Value::null(); | ||||
|         } | ||||
|         AK::StringBuilder builder; | ||||
|  | @ -111,7 +111,7 @@ Value BinaryOperatorExpression::evaluate(ExecutionContext& context) const | |||
|         auto lhs_bool_maybe = lhs_value.to_bool(); | ||||
|         auto rhs_bool_maybe = rhs_value.to_bool(); | ||||
|         if (!lhs_bool_maybe.has_value() || !rhs_bool_maybe.has_value()) { | ||||
|             context.result->set_error(SQLErrorCode::BooleanOperatorTypeMismatch, BinaryOperator_name(type())); | ||||
|             context.result = Result { SQLCommand::Unknown, SQLErrorCode::BooleanOperatorTypeMismatch, BinaryOperator_name(type()) }; | ||||
|             return Value::null(); | ||||
|         } | ||||
|         return Value(lhs_bool_maybe.release_value() && rhs_bool_maybe.release_value()); | ||||
|  | @ -120,7 +120,7 @@ Value BinaryOperatorExpression::evaluate(ExecutionContext& context) const | |||
|         auto lhs_bool_maybe = lhs_value.to_bool(); | ||||
|         auto rhs_bool_maybe = rhs_value.to_bool(); | ||||
|         if (!lhs_bool_maybe.has_value() || !rhs_bool_maybe.has_value()) { | ||||
|             context.result->set_error(SQLErrorCode::BooleanOperatorTypeMismatch, BinaryOperator_name(type())); | ||||
|             context.result = Result { SQLCommand::Unknown, SQLErrorCode::BooleanOperatorTypeMismatch, BinaryOperator_name(type()) }; | ||||
|             return Value::null(); | ||||
|         } | ||||
|         return Value(lhs_bool_maybe.release_value() || rhs_bool_maybe.release_value()); | ||||
|  | @ -132,14 +132,14 @@ Value BinaryOperatorExpression::evaluate(ExecutionContext& context) const | |||
| 
 | ||||
| Value UnaryOperatorExpression::evaluate(ExecutionContext& context) const | ||||
| { | ||||
|     if (context.result->has_error()) | ||||
|     if (context.result->is_error()) | ||||
|         return Value::null(); | ||||
|     Value expression_value = NestedExpression::evaluate(context); | ||||
|     switch (type()) { | ||||
|     case UnaryOperator::Plus: | ||||
|         if (expression_value.type() == SQLType::Integer || expression_value.type() == SQLType::Float) | ||||
|             return expression_value; | ||||
|         context.result->set_error(SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(type())); | ||||
|         context.result = Result { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(type()) }; | ||||
|         return Value::null(); | ||||
|     case UnaryOperator::Minus: | ||||
|         if (expression_value.type() == SQLType::Integer) { | ||||
|  | @ -150,21 +150,21 @@ Value UnaryOperatorExpression::evaluate(ExecutionContext& context) const | |||
|             expression_value = -double(expression_value); | ||||
|             return expression_value; | ||||
|         } | ||||
|         context.result->set_error(SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(type())); | ||||
|         context.result = Result { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(type()) }; | ||||
|         return Value::null(); | ||||
|     case UnaryOperator::Not: | ||||
|         if (expression_value.type() == SQLType::Boolean) { | ||||
|             expression_value = !bool(expression_value); | ||||
|             return expression_value; | ||||
|         } | ||||
|         context.result->set_error(SQLErrorCode::BooleanOperatorTypeMismatch, UnaryOperator_name(type())); | ||||
|         context.result = Result { SQLCommand::Unknown, SQLErrorCode::BooleanOperatorTypeMismatch, UnaryOperator_name(type()) }; | ||||
|         return Value::null(); | ||||
|     case UnaryOperator::BitwiseNot: | ||||
|         if (expression_value.type() == SQLType::Integer) { | ||||
|             expression_value = ~u32(expression_value); | ||||
|             return expression_value; | ||||
|         } | ||||
|         context.result->set_error(SQLErrorCode::IntegerOperatorTypeMismatch, UnaryOperator_name(type())); | ||||
|         context.result = Result { SQLCommand::Unknown, SQLErrorCode::IntegerOperatorTypeMismatch, UnaryOperator_name(type()) }; | ||||
|         return Value::null(); | ||||
|     } | ||||
|     VERIFY_NOT_REACHED(); | ||||
|  | @ -173,7 +173,7 @@ Value UnaryOperatorExpression::evaluate(ExecutionContext& context) const | |||
| Value ColumnNameExpression::evaluate(ExecutionContext& context) const | ||||
| { | ||||
|     if (!context.current_row) { | ||||
|         context.result->set_error(SQLErrorCode::SyntaxError, column_name()); | ||||
|         context.result = Result { SQLCommand::Unknown, SQLErrorCode::SyntaxError, column_name() }; | ||||
|         return Value::null(); | ||||
|     } | ||||
|     auto& descriptor = *context.current_row->descriptor(); | ||||
|  | @ -185,7 +185,7 @@ Value ColumnNameExpression::evaluate(ExecutionContext& context) const | |||
|             continue; | ||||
|         if (column_descriptor.name == column_name()) { | ||||
|             if (index_in_row.has_value()) { | ||||
|                 context.result->set_error(SQLErrorCode::AmbiguousColumnName, column_name()); | ||||
|                 context.result = Result { SQLCommand::Unknown, SQLErrorCode::AmbiguousColumnName, column_name() }; | ||||
|                 return Value::null(); | ||||
|             } | ||||
|             index_in_row = ix; | ||||
|  | @ -193,13 +193,13 @@ Value ColumnNameExpression::evaluate(ExecutionContext& context) const | |||
|     } | ||||
|     if (index_in_row.has_value()) | ||||
|         return (*context.current_row)[index_in_row.value()]; | ||||
|     context.result->set_error(SQLErrorCode::ColumnDoesNotExist, column_name()); | ||||
|     context.result = Result { SQLCommand::Unknown, SQLErrorCode::ColumnDoesNotExist, column_name() }; | ||||
|     return Value::null(); | ||||
| } | ||||
| 
 | ||||
| Value MatchExpression::evaluate(ExecutionContext& context) const | ||||
| { | ||||
|     if (context.result->has_error()) | ||||
|     if (context.result->is_error()) | ||||
|         return Value::null(); | ||||
|     switch (type()) { | ||||
|     case MatchOperator::Like: { | ||||
|  | @ -209,7 +209,7 @@ Value MatchExpression::evaluate(ExecutionContext& context) const | |||
|         if (escape()) { | ||||
|             auto escape_str = escape()->evaluate(context).to_string(); | ||||
|             if (escape_str.length() != 1) { | ||||
|                 context.result->set_error(SQLErrorCode::SyntaxError, "ESCAPE should be a single character"); | ||||
|                 context.result = Result { SQLCommand::Unknown, SQLErrorCode::SyntaxError, "ESCAPE should be a single character" }; | ||||
|                 return Value::null(); | ||||
|             } | ||||
|             escape_char = escape_str[0]; | ||||
|  | @ -253,7 +253,7 @@ Value MatchExpression::evaluate(ExecutionContext& context) const | |||
|             builder.append("Regular expression: "); | ||||
|             builder.append(get_error_string(err)); | ||||
| 
 | ||||
|             context.result->set_error(SQLErrorCode::SyntaxError, builder.build()); | ||||
|             context.result = Result { SQLCommand::Unknown, SQLErrorCode::SyntaxError, builder.build() }; | ||||
|             return Value(false); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,78 +14,69 @@ namespace SQL::AST { | |||
| 
 | ||||
| static bool does_value_data_type_match(SQLType expected, SQLType actual) | ||||
| { | ||||
|     if (actual == SQLType::Null) { | ||||
|     if (actual == SQLType::Null) | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (expected == SQLType::Integer) { | ||||
|     if (expected == SQLType::Integer) | ||||
|         return actual == SQLType::Integer || actual == SQLType::Float; | ||||
|     } | ||||
| 
 | ||||
|     return expected == actual; | ||||
| } | ||||
| 
 | ||||
| RefPtr<SQLResult> Insert::execute(ExecutionContext& context) const | ||||
| Result Insert::execute(ExecutionContext& context) const | ||||
| { | ||||
|     auto table_def_or_error = context.database->get_table(m_schema_name, m_table_name); | ||||
|     if (table_def_or_error.is_error()) | ||||
|         return SQLResult::construct(SQLCommand::Insert, SQLErrorCode::InternalError, table_def_or_error.release_error()); | ||||
|     auto table_def = table_def_or_error.release_value(); | ||||
|     auto table_def = TRY(context.database->get_table(m_schema_name, m_table_name)); | ||||
| 
 | ||||
|     if (!table_def) { | ||||
|         auto schema_name = m_schema_name; | ||||
|         if (schema_name.is_null() || schema_name.is_empty()) | ||||
|             schema_name = "default"; | ||||
|         return SQLResult::construct(SQLCommand::Insert, SQLErrorCode::TableDoesNotExist, String::formatted("{}.{}", schema_name, m_table_name)); | ||||
|         auto schema_name = m_schema_name.is_empty() ? String("default"sv) : m_schema_name; | ||||
|         return { SQLCommand::Insert, SQLErrorCode::TableDoesNotExist, String::formatted("{}.{}", schema_name, m_table_name) }; | ||||
|     } | ||||
| 
 | ||||
|     Row row(table_def); | ||||
|     for (auto& column : m_column_names) { | ||||
|         if (!row.has(column)) { | ||||
|             return SQLResult::construct(SQLCommand::Insert, SQLErrorCode::ColumnDoesNotExist, column); | ||||
|         } | ||||
|         if (!row.has(column)) | ||||
|             return { SQLCommand::Insert, SQLErrorCode::ColumnDoesNotExist, column }; | ||||
|     } | ||||
| 
 | ||||
|     Vector<Row> inserted_rows; | ||||
|     inserted_rows.ensure_capacity(m_chained_expressions.size()); | ||||
|     context.result = SQLResult::construct(); | ||||
|     TRY(inserted_rows.try_ensure_capacity(m_chained_expressions.size())); | ||||
| 
 | ||||
|     context.result = Result { SQLCommand::Insert }; | ||||
| 
 | ||||
|     for (auto& row_expr : m_chained_expressions) { | ||||
|         for (auto& column_def : table_def->columns()) { | ||||
|             if (!m_column_names.contains_slow(column_def.name())) { | ||||
|             if (!m_column_names.contains_slow(column_def.name())) | ||||
|                 row[column_def.name()] = column_def.default_value(); | ||||
|         } | ||||
|         } | ||||
| 
 | ||||
|         auto row_value = row_expr.evaluate(context); | ||||
|         if (context.result->has_error()) | ||||
|             return context.result; | ||||
|         if (context.result->is_error()) | ||||
|             return context.result.release_value(); | ||||
| 
 | ||||
|         VERIFY(row_value.type() == SQLType::Tuple); | ||||
|         auto values = row_value.to_vector().value(); | ||||
| 
 | ||||
|         if (m_column_names.size() == 0 && values.size() != row.size()) { | ||||
|             return SQLResult::construct(SQLCommand::Insert, SQLErrorCode::InvalidNumberOfValues, ""); | ||||
|         } | ||||
|         if (m_column_names.is_empty() && values.size() != row.size()) | ||||
|             return { SQLCommand::Insert, SQLErrorCode::InvalidNumberOfValues, String::empty() }; | ||||
| 
 | ||||
|         for (auto ix = 0u; ix < values.size(); ix++) { | ||||
|             auto input_value_type = values[ix].type(); | ||||
|             auto& tuple_descriptor = *row.descriptor(); | ||||
|             // In case of having column names, this must succeed since we checked for every column name for existence in the table.
 | ||||
|             auto element_index = (m_column_names.size() == 0) ? ix : tuple_descriptor.find_if([&](auto element) { return element.name == m_column_names[ix]; }).index(); | ||||
|             auto element_index = m_column_names.is_empty() ? ix : tuple_descriptor.find_if([&](auto element) { return element.name == m_column_names[ix]; }).index(); | ||||
|             auto element_type = tuple_descriptor[element_index].type; | ||||
| 
 | ||||
|             if (!does_value_data_type_match(element_type, input_value_type)) { | ||||
|                 return SQLResult::construct(SQLCommand::Insert, SQLErrorCode::InvalidValueType, table_def->columns()[element_index].name()); | ||||
|             } | ||||
|             if (!does_value_data_type_match(element_type, input_value_type)) | ||||
|                 return { SQLCommand::Insert, SQLErrorCode::InvalidValueType, table_def->columns()[element_index].name() }; | ||||
| 
 | ||||
|             row[element_index] = values[ix]; | ||||
|         } | ||||
| 
 | ||||
|         inserted_rows.append(row); | ||||
|     } | ||||
| 
 | ||||
|     for (auto& inserted_row : inserted_rows) { | ||||
|         if (auto maybe_error = context.database->insert(inserted_row); maybe_error.is_error()) | ||||
|             return SQLResult::construct(SQLCommand::Insert, SQLErrorCode::InternalError, maybe_error.release_error()); | ||||
|     } | ||||
|     for (auto& inserted_row : inserted_rows) | ||||
|         TRY(context.database->insert(inserted_row)); | ||||
| 
 | ||||
|     return SQLResult::construct(SQLCommand::Insert, 0, m_chained_expressions.size(), 0); | ||||
|     return { SQLCommand::Insert, 0, m_chained_expressions.size() }; | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -13,65 +13,67 @@ | |||
| 
 | ||||
| namespace SQL::AST { | ||||
| 
 | ||||
| RefPtr<SQLResult> Select::execute(ExecutionContext& context) const | ||||
| Result Select::execute(ExecutionContext& context) const | ||||
| { | ||||
|     NonnullRefPtrVector<ResultColumn> columns; | ||||
| 
 | ||||
|     auto const& result_column_list = this->result_column_list(); | ||||
|     VERIFY(!result_column_list.is_empty()); | ||||
| 
 | ||||
|     for (auto& table_descriptor : table_or_subquery_list()) { | ||||
|         if (!table_descriptor.is_table()) | ||||
|             return SQLResult::construct(SQLCommand::Select, SQLErrorCode::NotYetImplemented, "Sub-selects are not yet implemented"); | ||||
|         auto table_def_or_error = context.database->get_table(table_descriptor.schema_name(), table_descriptor.table_name()); | ||||
|         if (table_def_or_error.is_error()) | ||||
|             return SQLResult::construct(SQLCommand::Select, SQLErrorCode::InternalError, table_def_or_error.error()); | ||||
|         auto table = table_def_or_error.value(); | ||||
|         if (!table) { | ||||
|             return SQLResult::construct(SQLCommand::Select, SQLErrorCode::TableDoesNotExist, table_descriptor.table_name()); | ||||
|         } | ||||
|             return { SQLCommand::Select, SQLErrorCode::NotYetImplemented, "Sub-selects are not yet implemented"sv }; | ||||
| 
 | ||||
|         if (result_column_list().size() == 1 && result_column_list()[0].type() == ResultType::All) { | ||||
|             for (auto& col : table->columns()) { | ||||
|         auto table_def = TRY(context.database->get_table(table_descriptor.schema_name(), table_descriptor.table_name())); | ||||
|         if (!table_def) | ||||
|             return { SQLCommand::Select, SQLErrorCode::TableDoesNotExist, table_descriptor.table_name() }; | ||||
| 
 | ||||
|         if (result_column_list.size() == 1 && result_column_list[0].type() == ResultType::All) { | ||||
|             for (auto& col : table_def->columns()) { | ||||
|                 columns.append( | ||||
|                     create_ast_node<ResultColumn>( | ||||
|                         create_ast_node<ColumnNameExpression>(table->parent()->name(), table->name(), col.name()), | ||||
|                         create_ast_node<ColumnNameExpression>(table_def->parent()->name(), table_def->name(), col.name()), | ||||
|                         "")); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     VERIFY(!result_column_list().is_empty()); | ||||
|     if (result_column_list().size() != 1 || result_column_list()[0].type() != ResultType::All) { | ||||
|         for (auto& col : result_column_list()) { | ||||
|             if (col.type() == ResultType::All) | ||||
|     if (result_column_list.size() != 1 || result_column_list[0].type() != ResultType::All) { | ||||
|         for (auto& col : result_column_list) { | ||||
|             if (col.type() == ResultType::All) { | ||||
|                 // FIXME can have '*' for example in conjunction with computed columns
 | ||||
|                 return SQLResult::construct(SQL::SQLCommand::Select, SQLErrorCode::SyntaxError, "*"); | ||||
|                 return { SQLCommand::Select, SQLErrorCode::SyntaxError, "*"sv }; | ||||
|             } | ||||
| 
 | ||||
|             columns.append(col); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     context.result = SQLResult::construct(); | ||||
|     AK::NonnullRefPtr<TupleDescriptor> descriptor = AK::adopt_ref(*new TupleDescriptor); | ||||
|     context.result = Result { SQLCommand::Select }; | ||||
| 
 | ||||
|     auto descriptor = adopt_ref(*new TupleDescriptor); | ||||
|     Tuple tuple(descriptor); | ||||
|     Vector<Tuple> rows; | ||||
|     descriptor->empend("__unity__"); | ||||
|     descriptor->empend("__unity__"sv); | ||||
|     tuple.append(Value(SQLType::Boolean, true)); | ||||
|     rows.append(tuple); | ||||
| 
 | ||||
|     for (auto& table_descriptor : table_or_subquery_list()) { | ||||
|         if (!table_descriptor.is_table()) | ||||
|             return SQLResult::construct(SQLCommand::Select, SQLErrorCode::NotYetImplemented, "Sub-selects are not yet implemented"); | ||||
|         auto table_def_or_error = context.database->get_table(table_descriptor.schema_name(), table_descriptor.table_name()); | ||||
|         if (table_def_or_error.is_error()) | ||||
|             return SQLResult::construct(SQLCommand::Select, SQLErrorCode::InternalError, table_def_or_error.error()); | ||||
|         auto table = table_def_or_error.value(); | ||||
|         if (table->num_columns() == 0) | ||||
|             return { SQLCommand::Select, SQLErrorCode::NotYetImplemented, "Sub-selects are not yet implemented"sv }; | ||||
| 
 | ||||
|         auto table_def = TRY(context.database->get_table(table_descriptor.schema_name(), table_descriptor.table_name())); | ||||
|         if (table_def->num_columns() == 0) | ||||
|             continue; | ||||
| 
 | ||||
|         auto old_descriptor_size = descriptor->size(); | ||||
|         descriptor->extend(table->to_tuple_descriptor()); | ||||
|         descriptor->extend(table_def->to_tuple_descriptor()); | ||||
| 
 | ||||
|         for (auto cartesian_row = rows.first(); cartesian_row.size() == old_descriptor_size; cartesian_row = rows.first()) { | ||||
|             rows.remove(0); | ||||
|             auto table_rows_or_error = context.database->select_all(*table); | ||||
|             if (table_rows_or_error.is_error()) | ||||
|                 return SQLResult::construct(SQLCommand::Select, SQLErrorCode::InternalError, table_rows_or_error.error()); | ||||
|             for (auto& table_row : table_rows_or_error.value()) { | ||||
|             auto table_rows = TRY(context.database->select_all(*table_def)); | ||||
| 
 | ||||
|             for (auto& table_row : table_rows) { | ||||
|                 auto new_row = cartesian_row; | ||||
|                 new_row.extend(table_row); | ||||
|                 rows.append(new_row); | ||||
|  | @ -80,7 +82,7 @@ RefPtr<SQLResult> Select::execute(ExecutionContext& context) const | |||
|     } | ||||
| 
 | ||||
|     bool has_ordering { false }; | ||||
|     AK::NonnullRefPtr<TupleDescriptor> sort_descriptor = AK::adopt_ref(*new TupleDescriptor); | ||||
|     auto sort_descriptor = adopt_ref(*new TupleDescriptor); | ||||
|     for (auto& term : m_ordering_term_list) { | ||||
|         sort_descriptor->append(TupleElementDescriptor { .order = term.order() }); | ||||
|         has_ordering = true; | ||||
|  | @ -89,18 +91,21 @@ RefPtr<SQLResult> Select::execute(ExecutionContext& context) const | |||
| 
 | ||||
|     for (auto& row : rows) { | ||||
|         context.current_row = &row; | ||||
| 
 | ||||
|         if (where_clause()) { | ||||
|             auto where_result = where_clause()->evaluate(context); | ||||
|             if (context.result->has_error()) | ||||
|                 return context.result; | ||||
|             if (context.result->is_error()) | ||||
|                 return context.result.release_value(); | ||||
|             if (!where_result) | ||||
|                 continue; | ||||
|         } | ||||
| 
 | ||||
|         tuple.clear(); | ||||
| 
 | ||||
|         for (auto& col : columns) { | ||||
|             auto value = col.expression()->evaluate(context); | ||||
|             if (context.result->has_error()) | ||||
|                 return context.result; | ||||
|             if (context.result->is_error()) | ||||
|                 return context.result.release_value(); | ||||
|             tuple.append(value); | ||||
|         } | ||||
| 
 | ||||
|  | @ -108,39 +113,43 @@ RefPtr<SQLResult> Select::execute(ExecutionContext& context) const | |||
|             sort_key.clear(); | ||||
|             for (auto& term : m_ordering_term_list) { | ||||
|                 auto value = term.expression()->evaluate(context); | ||||
|                 if (context.result->has_error()) | ||||
|                     return context.result; | ||||
|                 if (context.result->is_error()) | ||||
|                     return context.result.release_value(); | ||||
|                 sort_key.append(value); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         context.result->insert(tuple, sort_key); | ||||
|     } | ||||
| 
 | ||||
|     if (m_limit_clause != nullptr) { | ||||
|         size_t limit_value = NumericLimits<size_t>::max(); | ||||
|         size_t offset_value = 0; | ||||
| 
 | ||||
|         auto limit = m_limit_clause->limit_expression()->evaluate(context); | ||||
|         if (!limit.is_null()) { | ||||
|             auto limit_value_maybe = limit.to_u32(); | ||||
|             if (!limit_value_maybe.has_value()) { | ||||
|                 return SQLResult::construct(SQLCommand::Select, SQLErrorCode::SyntaxError, "LIMIT clause must evaluate to an integer value"); | ||||
|             } | ||||
|             if (!limit_value_maybe.has_value()) | ||||
|                 return { SQLCommand::Select, SQLErrorCode::SyntaxError, "LIMIT clause must evaluate to an integer value"sv }; | ||||
| 
 | ||||
|             limit_value = limit_value_maybe.value(); | ||||
|         } | ||||
|         size_t offset_value = 0; | ||||
| 
 | ||||
|         if (m_limit_clause->offset_expression() != nullptr) { | ||||
|             auto offset = m_limit_clause->offset_expression()->evaluate(context); | ||||
|             if (!offset.is_null()) { | ||||
|                 auto offset_value_maybe = offset.to_u32(); | ||||
|                 if (!offset_value_maybe.has_value()) { | ||||
|                     return SQLResult::construct(SQLCommand::Select, SQLErrorCode::SyntaxError, "OFFSET clause must evaluate to an integer value"); | ||||
|                 } | ||||
|                 if (!offset_value_maybe.has_value()) | ||||
|                     return { SQLCommand::Select, SQLErrorCode::SyntaxError, "OFFSET clause must evaluate to an integer value"sv }; | ||||
| 
 | ||||
|                 offset_value = offset_value_maybe.value(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         context.result->limit(offset_value, limit_value); | ||||
|     } | ||||
| 
 | ||||
|     return context.result; | ||||
|     return context.result.release_value(); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -11,9 +11,9 @@ | |||
| 
 | ||||
| namespace SQL::AST { | ||||
| 
 | ||||
| RefPtr<SQLResult> Statement::execute(AK::NonnullRefPtr<Database> database) const | ||||
| Result Statement::execute(AK::NonnullRefPtr<Database> database) const | ||||
| { | ||||
|     ExecutionContext context { move(database), nullptr, this, nullptr }; | ||||
|     ExecutionContext context { move(database), {}, this, nullptr }; | ||||
|     return execute(context); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -33,22 +33,22 @@ SQLStatement::SQLStatement(DatabaseConnection& connection, String sql) | |||
|     s_statements.set(m_statement_id, *this); | ||||
| } | ||||
| 
 | ||||
| void SQLStatement::report_error(SQL::SQLError error) | ||||
| void SQLStatement::report_error() | ||||
| { | ||||
|     dbgln_if(SQLSERVER_DEBUG, "SQLStatement::report_error(statement_id {}, error {}", statement_id(), error.to_string()); | ||||
|     dbgln_if(SQLSERVER_DEBUG, "SQLStatement::report_error(statement_id {}, error {}", statement_id(), m_result->error_string()); | ||||
| 
 | ||||
|     auto client_connection = ClientConnection::client_connection_for(connection()->client_id()); | ||||
|     m_statement = nullptr; | ||||
|     m_result = nullptr; | ||||
|     remove_from_parent(); | ||||
| 
 | ||||
|     s_statements.remove(statement_id()); | ||||
|     if (!client_connection) { | ||||
|     remove_from_parent(); | ||||
| 
 | ||||
|     if (client_connection) | ||||
|         client_connection->async_execution_error(statement_id(), (int)m_result->error(), m_result->error_string()); | ||||
|     else | ||||
|         warnln("Cannot return execution error. Client disconnected"); | ||||
|         warnln("SQLStatement::report_error(statement_id {}, error {}", statement_id(), error.to_string()); | ||||
|         m_result = nullptr; | ||||
|         return; | ||||
|     } | ||||
|     client_connection->async_execution_error(statement_id(), (int)error.code, error.to_string()); | ||||
|     m_result = nullptr; | ||||
| 
 | ||||
|     m_statement = nullptr; | ||||
|     m_result = {}; | ||||
| } | ||||
| 
 | ||||
| void SQLStatement::execute() | ||||
|  | @ -60,18 +60,21 @@ void SQLStatement::execute() | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     deferred_invoke([this]() { | ||||
|         auto maybe_error = parse(); | ||||
|         if (maybe_error.has_value()) { | ||||
|             report_error(maybe_error.value()); | ||||
|     deferred_invoke([this]() mutable { | ||||
|         m_result = parse(); | ||||
|         if (m_result.has_value()) { | ||||
|             report_error(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         VERIFY(!connection()->database().is_null()); | ||||
| 
 | ||||
|         m_result = m_statement->execute(connection()->database().release_nonnull()); | ||||
|         if (m_result->error().code != SQL::SQLErrorCode::NoError) { | ||||
|             report_error(m_result->error()); | ||||
|         if (m_result->is_error()) { | ||||
|             report_error(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         auto client_connection = ClientConnection::client_connection_for(connection()->client_id()); | ||||
|         if (!client_connection) { | ||||
|             warnln("Cannot return statement execution results. Client disconnected"); | ||||
|  | @ -85,13 +88,13 @@ void SQLStatement::execute() | |||
|     }); | ||||
| } | ||||
| 
 | ||||
| Optional<SQL::SQLError> SQLStatement::parse() | ||||
| Optional<SQL::Result> SQLStatement::parse() | ||||
| { | ||||
|     auto parser = SQL::AST::Parser(SQL::AST::Lexer(m_sql)); | ||||
|     m_statement = parser.next_statement(); | ||||
|     if (parser.has_errors()) { | ||||
|         return SQL::SQLError { SQL::SQLErrorCode::SyntaxError, parser.errors()[0].to_string() }; | ||||
|     } | ||||
| 
 | ||||
|     if (parser.has_errors()) | ||||
|         return SQL::Result { SQL::SQLCommand::Unknown, SQL::SQLErrorCode::SyntaxError, parser.errors()[0].to_string() }; | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,15 +30,15 @@ public: | |||
| 
 | ||||
| private: | ||||
|     SQLStatement(DatabaseConnection&, String sql); | ||||
|     Optional<SQL::SQLError> parse(); | ||||
|     Optional<SQL::Result> parse(); | ||||
|     void next(); | ||||
|     void report_error(SQL::SQLError); | ||||
|     void report_error(); | ||||
| 
 | ||||
|     int m_statement_id; | ||||
|     String m_sql; | ||||
|     size_t m_index { 0 }; | ||||
|     RefPtr<SQL::AST::Statement> m_statement { nullptr }; | ||||
|     RefPtr<SQL::SQLResult> m_result { nullptr }; | ||||
|     Optional<SQL::Result> m_result {}; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy Flynn
						Timothy Flynn