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