mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 05:48:12 +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