ladybird/Tests/LibSQL/TestSqlStatementExecution.cpp
Jan de Visser 1c50e9aadc LibSQL: Add current statement to the ExecutionContext
Because SQL is the craptastic language that it is, sometimes expressions
need to know details about the calling statement. For example the tables
in the 'FROM' clause may be needed to determine which columns are
referenced in 'WHERE' expressions. So the current statement is added
to the ExecutionContext and a new 'execute' overload on Statement is
created which takes the Database and the Statement and builds an
ExecutionContaxt from those.
2021-11-10 14:47:49 +01:00

212 lines
8 KiB
C++

/*
* Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <unistd.h>
#include <AK/ScopeGuard.h>
#include <LibSQL/AST/Parser.h>
#include <LibSQL/Database.h>
#include <LibSQL/Row.h>
#include <LibSQL/SQLResult.h>
#include <LibSQL/Value.h>
#include <LibTest/TestCase.h>
namespace {
constexpr const char* db_name = "/tmp/test.db";
RefPtr<SQL::SQLResult> execute(NonnullRefPtr<SQL::Database> database, String const& sql)
{
auto parser = SQL::AST::Parser(SQL::AST::Lexer(sql));
auto statement = parser.next_statement();
EXPECT(!parser.has_errors());
if (parser.has_errors())
outln("{}", parser.errors()[0].to_string());
auto result = statement->execute(move(database));
if (result->error().code != SQL::SQLErrorCode::NoError)
outln("{}", result->error().to_string());
return result;
}
void create_schema(NonnullRefPtr<SQL::Database> database)
{
auto result = execute(database, "CREATE SCHEMA TestSchema;");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->inserted() == 1);
}
void create_table(NonnullRefPtr<SQL::Database> database)
{
create_schema(database);
auto result = execute(database, "CREATE TABLE TestSchema.TestTable ( TextColumn text, IntColumn integer );");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->inserted() == 1);
}
TEST_CASE(create_schema)
{
ScopeGuard guard([]() { unlink(db_name); });
auto database = SQL::Database::construct(db_name);
create_schema(database);
auto schema = database->get_schema("TESTSCHEMA");
EXPECT(schema);
}
TEST_CASE(create_table)
{
ScopeGuard guard([]() { unlink(db_name); });
auto database = SQL::Database::construct(db_name);
create_table(database);
auto table = database->get_table("TESTSCHEMA", "TESTTABLE");
EXPECT(table);
}
TEST_CASE(insert_into_table)
{
ScopeGuard guard([]() { unlink(db_name); });
auto database = SQL::Database::construct(db_name);
create_table(database);
auto result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test', 42 );");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->inserted() == 1);
auto table = database->get_table("TESTSCHEMA", "TESTTABLE");
int count = 0;
for (auto& row : database->select_all(*table)) {
EXPECT_EQ(row["TEXTCOLUMN"].to_string(), "Test");
EXPECT_EQ(row["INTCOLUMN"].to_int().value(), 42);
count++;
}
EXPECT_EQ(count, 1);
}
TEST_CASE(insert_into_table_wrong_data_types)
{
ScopeGuard guard([]() { unlink(db_name); });
auto database = SQL::Database::construct(db_name);
create_table(database);
auto result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES (43, 'Test_2');");
EXPECT(result->inserted() == 0);
EXPECT(result->error().code == SQL::SQLErrorCode::InvalidValueType);
}
TEST_CASE(insert_into_table_multiple_tuples_wrong_data_types)
{
ScopeGuard guard([]() { unlink(db_name); });
auto database = SQL::Database::construct(db_name);
create_table(database);
auto result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ('Test_1', 42), (43, 'Test_2');");
EXPECT(result->inserted() == 0);
EXPECT(result->error().code == SQL::SQLErrorCode::InvalidValueType);
}
TEST_CASE(insert_wrong_number_of_values)
{
ScopeGuard guard([]() { unlink(db_name); });
auto database = SQL::Database::construct(db_name);
create_table(database);
auto result = execute(database, "INSERT INTO TestSchema.TestTable VALUES ( 42 );");
EXPECT(result->error().code == SQL::SQLErrorCode::InvalidNumberOfValues);
EXPECT(result->inserted() == 0);
}
TEST_CASE(insert_without_column_names)
{
ScopeGuard guard([]() { unlink(db_name); });
auto database = SQL::Database::construct(db_name);
create_table(database);
auto result = execute(database, "INSERT INTO TestSchema.TestTable VALUES ('Test_1', 42), ('Test_2', 43);");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->inserted() == 2);
auto table = database->get_table("TESTSCHEMA", "TESTTABLE");
EXPECT_EQ(database->select_all(*table).size(), 2u);
}
TEST_CASE(select_from_table)
{
ScopeGuard guard([]() { unlink(db_name); });
auto database = SQL::Database::construct(db_name);
create_table(database);
auto result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_1', 42 ), ( 'Test_2', 43 );");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->inserted() == 2);
result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_3', 44 ), ( 'Test_4', 45 );");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->inserted() == 2);
result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_5', 46 );");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->inserted() == 1);
result = execute(database, "SELECT * FROM TestSchema.TestTable;");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->has_results());
EXPECT_EQ(result->results().size(), 5u);
}
TEST_CASE(select_with_column_names)
{
ScopeGuard guard([]() { unlink(db_name); });
auto database = SQL::Database::construct(db_name);
create_table(database);
auto result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_1', 42 ), ( 'Test_2', 43 );");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->inserted() == 2);
result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_3', 44 ), ( 'Test_4', 45 );");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->inserted() == 2);
result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_5', 46 );");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->inserted() == 1);
result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable;");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->has_results());
EXPECT_EQ(result->results().size(), 5u);
EXPECT_EQ(result->results()[0].size(), 1u);
}
TEST_CASE(select_with_nonexisting_column_name)
{
ScopeGuard guard([]() { unlink(db_name); });
auto database = SQL::Database::construct(db_name);
create_table(database);
auto result = execute(database,
"INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES "
"( 'Test_1', 42 ), "
"( 'Test_2', 43 ), "
"( 'Test_3', 44 ), "
"( 'Test_4', 45 ), "
"( 'Test_5', 46 );");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->inserted() == 5);
result = execute(database, "SELECT Bogus FROM TestSchema.TestTable;");
EXPECT(result->error().code == SQL::SQLErrorCode::ColumnDoesNotExist);
}
TEST_CASE(select_with_where)
{
ScopeGuard guard([]() { unlink(db_name); });
auto database = SQL::Database::construct(db_name);
create_table(database);
auto result = execute(database,
"INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES "
"( 'Test_1', 42 ), "
"( 'Test_2', 43 ), "
"( 'Test_3', 44 ), "
"( 'Test_4', 45 ), "
"( 'Test_5', 46 );");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->inserted() == 5);
result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable WHERE IntColumn > 44;");
EXPECT(result->error().code == SQL::SQLErrorCode::NoError);
EXPECT(result->has_results());
EXPECT_EQ(result->results().size(), 2u);
for (auto& row : result->results()) {
EXPECT(row[1].to_int().value() > 44);
}
}
}