summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibSQL/AST/Select.cpp
blob: 8dfa6a93cc8db0329f890fa9f53b315d4a49dbf6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
 * Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <AK/NumericLimits.h>
#include <LibSQL/AST/AST.h>
#include <LibSQL/Database.h>
#include <LibSQL/Meta.h>
#include <LibSQL/Row.h>

namespace SQL::AST {

ResultOr<ResultSet> Select::execute(ExecutionContext& context) const
{
    NonnullRefPtrVector<ResultColumn> columns;

    auto const& result_column_list = this->result_column_list();
    VERIFY(!result_column_list.is_empty());

    for (auto& table_descriptor : table_or_subquery_list()) {
        if (!table_descriptor.is_table())
            return Result { SQLCommand::Select, SQLErrorCode::NotYetImplemented, "Sub-selects are not yet implemented"sv };

        auto table_def = TRY(context.database->get_table(table_descriptor.schema_name(), table_descriptor.table_name()));

        if (result_column_list.size() == 1 && result_column_list[0].type() == ResultType::All) {
            for (auto& col : table_def->columns()) {
                columns.append(
                    create_ast_node<ResultColumn>(
                        create_ast_node<ColumnNameExpression>(table_def->parent()->name(), table_def->name(), col.name()),
                        ""));
            }
        }
    }

    if (result_column_list.size() != 1 || result_column_list[0].type() != ResultType::All) {
        for (auto& col : result_column_list) {
            if (col.type() == ResultType::All) {
                // FIXME can have '*' for example in conjunction with computed columns
                return Result { SQLCommand::Select, SQLErrorCode::SyntaxError, "*"sv };
            }

            columns.append(col);
        }
    }

    ResultSet result { SQLCommand::Select };

    auto descriptor = adopt_ref(*new TupleDescriptor);
    Tuple tuple(descriptor);
    Vector<Tuple> rows;
    descriptor->empend("__unity__"sv);
    tuple.append(Value { true });
    rows.append(tuple);

    for (auto& table_descriptor : table_or_subquery_list()) {
        if (!table_descriptor.is_table())
            return Result { SQLCommand::Select, SQLErrorCode::NotYetImplemented, "Sub-selects are not yet implemented"sv };

        auto table_def = TRY(context.database->get_table(table_descriptor.schema_name(), table_descriptor.table_name()));
        if (table_def->num_columns() == 0)
            continue;

        auto old_descriptor_size = descriptor->size();
        descriptor->extend(table_def->to_tuple_descriptor());

        while (!rows.is_empty() && (rows.first().size() == old_descriptor_size)) {
            auto cartesian_row = rows.take_first();
            auto table_rows = TRY(context.database->select_all(*table_def));

            for (auto& table_row : table_rows) {
                auto new_row = cartesian_row;
                new_row.extend(table_row);
                rows.append(new_row);
            }
        }
    }

    bool has_ordering { false };
    auto sort_descriptor = adopt_ref(*new TupleDescriptor);
    for (auto& term : m_ordering_term_list) {
        sort_descriptor->append(TupleElementDescriptor { .order = term.order() });
        has_ordering = true;
    }
    Tuple sort_key(sort_descriptor);

    for (auto& row : rows) {
        context.current_row = &row;

        if (where_clause()) {
            auto where_result = TRY(where_clause()->evaluate(context)).to_bool();
            if (!where_result.has_value() || !where_result.value())
                continue;
        }

        tuple.clear();

        for (auto& col : columns) {
            auto value = TRY(col.expression()->evaluate(context));
            tuple.append(value);
        }

        if (has_ordering) {
            sort_key.clear();
            for (auto& term : m_ordering_term_list) {
                auto value = TRY(term.expression()->evaluate(context));
                sort_key.append(value);
            }
        }

        result.insert_row(tuple, sort_key);
    }

    if (m_limit_clause != nullptr) {
        size_t limit_value = NumericLimits<size_t>::max();
        size_t offset_value = 0;

        auto limit = TRY(m_limit_clause->limit_expression()->evaluate(context));
        if (!limit.is_null()) {
            auto limit_value_maybe = limit.to_u32();
            if (!limit_value_maybe.has_value())
                return Result { SQLCommand::Select, SQLErrorCode::SyntaxError, "LIMIT clause must evaluate to an integer value"sv };

            limit_value = limit_value_maybe.value();
        }

        if (m_limit_clause->offset_expression() != nullptr) {
            auto offset = TRY(m_limit_clause->offset_expression()->evaluate(context));
            if (!offset.is_null()) {
                auto offset_value_maybe = offset.to_u32();
                if (!offset_value_maybe.has_value())
                    return Result { SQLCommand::Select, SQLErrorCode::SyntaxError, "OFFSET clause must evaluate to an integer value"sv };

                offset_value = offset_value_maybe.value();
            }
        }

        result.limit(offset_value, limit_value);
    }

    return result;
}

}