Lines 100.00% 51 / 51
Functions and Methods 100.00% 1 / 1
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
SqlFeature 100.00% 51 / 51 100.00% 1 / 1 30 100.00% 1 / 1
 usedBy 100.00% 51 / 51 100.00% 1 / 1 30
1<?php
2
3declare(strict_types=1);
4
5namespace BlueprintAU\Radiant\Database\Query;
6
7/**
8 * The SQL features a query builder can carry — the introspection surface
9 * for feature-gating before execution.
10 *
11 * @package BlueprintAU\Radiant\Database\Query
12 */
13enum SqlFeature: string
14{
15    /** The query carries JOIN clauses (including MTI ancestor joins). */
16    case Joins = 'joins';
17
18    /** The query carries HAVING clauses. */
19    case Having = 'having';
20
21    /** The query filters on an {@see Aggregate} anywhere. */
22    case Aggregates = 'aggregates';
23
24    /** The query carries raw SQL fragments (whereRaw, Expression). */
25    case RawSql = 'raw-sql';
26
27    /** The query selects from a subquery (fromSub). */
28    case SubqueryFrom = 'subquery-from';
29
30    /** The query filters on an EXISTS (or NOT EXISTS) subquery (whereExists). */
31    case SubqueryWhere = 'subquery-where';
32
33    /** The query selects a scalar subquery column (selectSub). */
34    case SubquerySelect = 'subquery-select';
35
36    /** The query UNIONs another query. */
37    case Unions = 'unions';
38
39    /** The query requests a row lock (lockForUpdate / sharedLock). */
40    case RowLocks = 'row-locks';
41
42    /** The query is DISTINCT. */
43    case Distinct = 'distinct';
44
45    /**
46     * Which features this query uses, as a set.
47     *
48     * Single pass: one foreach per state list classifies every entry — no
49     * per-feature array_filter materializing throwaway arrays.
50     *
51     * @param  \BlueprintAU\Radiant\Database\Query\QueryBuilder  $query
52     * @return list<self>
53     */
54    public static function usedBy(QueryBuilder $query): array
55    {
56        $rawWhere = false;
57        $subqueryWhere = false;
58        $rawOrderBy = false;
59        $rawSelect = false;
60        $subquerySelect = false;
61        $aggregateSelect = false;
62        $rawHaving = false;
63        $aggregateHaving = false;
64
65        foreach ($query->getWheres() as $where) {
66            match ($where['type']) {
67                \BlueprintAU\Radiant\Database\Query\Enums\WhereType::Raw => $rawWhere = true,
68                \BlueprintAU\Radiant\Database\Query\Enums\WhereType::Exists,
69                \BlueprintAU\Radiant\Database\Query\Enums\WhereType::InSub => $subqueryWhere = true,
70                default => null,
71            };
72        }
73
74        foreach ($query->getColumns() as $column) {
75            if ($column instanceof Expression) {
76                $rawSelect = true;
77            } elseif ($column instanceof SubquerySelect) {
78                $subquerySelect = true;
79            } elseif ($column instanceof Aggregate) {
80                $aggregateSelect = true;
81            }
82        }
83
84        foreach ($query->getHavings() as $having) {
85            if ($having['column'] instanceof Expression) {
86                $rawHaving = true;
87            } elseif ($having['column'] instanceof Aggregate) {
88                $aggregateHaving = true;
89            }
90        }
91
92        foreach ($query->getOrders() as $order) {
93            if ($order['column'] instanceof Expression) {
94                $rawOrderBy = true;
95            }
96        }
97
98        $used = [];
99
100        if ($query->getJoins() !== []) {
101            $used[] = self::Joins;
102        }
103        if ($query->getHavings() !== [] || $aggregateHaving) {
104            $used[] = self::Having;
105        }
106        if ($query->getGroups() !== [] || $aggregateSelect || $aggregateHaving) {
107            $used[] = self::Aggregates;
108        }
109        if ($rawWhere || $rawOrderBy || $rawSelect || $rawHaving) {
110            $used[] = self::RawSql;
111        }
112        if ($query->getFromAlias() !== null) {
113            $used[] = self::SubqueryFrom;
114        }
115        if ($subqueryWhere) {
116            $used[] = self::SubqueryWhere;
117        }
118        if ($subquerySelect) {
119            $used[] = self::SubquerySelect;
120        }
121        if ($query->getUnions() !== []) {
122            $used[] = self::Unions;
123        }
124        if ($query->getLock() !== null) {
125            $used[] = self::RowLocks;
126        }
127        if ($query->isDistinct()) {
128            $used[] = self::Distinct;
129        }
130
131        return $used;
132    }
133}