Line data Source code
1 : //! Identify constant expressions.
2 : //!
3 : //! Literals, constant interval casts, binary expressions and comparisons over constants, and
4 : //! function calls without modifiers, FILTER and ORDER BY over constants are constant. Finish stores the roots of the
5 : //! constant expression trees.
6 :
7 : use super::state::{literal_type, AnalysisState};
8 : use super::types::*;
9 : use dashql_buffers::algebra::{BinaryExpressionFunction, ComparisonFunction};
10 : use dashql_buffers::analyzer::SemanticNodeMarkerType;
11 : use dashql_buffers::parser::{AttributeKey as K, ExpressionOperator as Op, IntervalType as IntervalTypeEnum, NodeType};
12 : use dashql_parser::attrs;
13 : use std::ops::Range;
14 :
15 : /// The pass that identifies constant expressions
16 : #[derive(Default)]
17 : pub(super) struct ConstantPropagationPass {
18 : /// The constant expressions, stored as roots during Finish
19 : constants: Vec<ExprId>,
20 : }
21 :
22 : /// The binary expression function of an operator, UNKNOWN for operators without one
23 38 : fn binary_function(op: Op) -> BinaryExpressionFunction {
24 38 : match op {
25 26 : Op::PLUS => BinaryExpressionFunction::PLUS,
26 6 : Op::MINUS => BinaryExpressionFunction::MINUS,
27 3 : Op::MULTIPLY => BinaryExpressionFunction::MULTIPLY,
28 1 : Op::DIVIDE => BinaryExpressionFunction::DIVIDE,
29 1 : Op::MODULUS => BinaryExpressionFunction::MODULUS,
30 1 : Op::XOR => BinaryExpressionFunction::XOR,
31 0 : _ => BinaryExpressionFunction::UNKNOWN,
32 : }
33 38 : }
34 :
35 : /// The comparison function of an operator, UNKNOWN for operators without one
36 8 : fn comparison_function(op: Op) -> ComparisonFunction {
37 8 : match op {
38 2 : Op::EQUAL => ComparisonFunction::EQUAL,
39 1 : Op::NOT_EQUAL => ComparisonFunction::NOT_EQUAL,
40 1 : Op::LESS_EQUAL => ComparisonFunction::LESS_EQUAL,
41 2 : Op::LESS_THAN => ComparisonFunction::LESS_THAN,
42 1 : Op::GREATER_EQUAL => ComparisonFunction::GREATER_EQUAL,
43 1 : Op::GREATER_THAN => ComparisonFunction::GREATER_THAN,
44 0 : _ => ComparisonFunction::UNKNOWN,
45 : }
46 8 : }
47 :
48 : impl ConstantPropagationPass {
49 : /// The constant expression of a node
50 989 : fn constant_of(state: &AnalysisState, node_id: u32) -> Option<ExprId> {
51 989 : state.expression_of(node_id).filter(|&e| state.out.expressions[e as usize].is_constant_expression)
52 989 : }
53 :
54 2856 : fn add_constant(&mut self, state: &mut AnalysisState, node_id: u32, inner: ExpressionInner, marker: SemanticNodeMarkerType) {
55 2856 : let span = *state.ast[node_id as usize].symbol_span();
56 2856 : let expr = state.add_expression(node_id, span, inner);
57 2856 : state.out.expressions[expr as usize].is_constant_expression = true;
58 2856 : state.expression_index[node_id as usize] = Some(expr);
59 2856 : state.mark(node_id, marker);
60 2856 : self.constants.push(expr);
61 2856 : }
62 :
63 3589 : pub fn visit(&mut self, state: &mut AnalysisState, morsel: Range<usize>) {
64 78912 : for node_id in morsel {
65 78912 : let node = &state.ast[node_id];
66 78912 : let node_id = node_id as u32;
67 78912 : match node.node_type() {
68 : // Base case, literals
69 : NodeType::LITERAL_FLOAT
70 : | NodeType::LITERAL_INTEGER
71 : | NodeType::LITERAL_INTERVAL
72 : | NodeType::LITERAL_NULL
73 2801 : | NodeType::LITERAL_STRING => {
74 2801 : let raw_value = state.text_span(*node.symbol_span());
75 2801 : let inner = ExpressionInner::Literal(Literal {
76 2801 : literal_type: literal_type(node.node_type()),
77 2801 : raw_value,
78 2801 : });
79 2801 : // The literal node types share the values of the semantic markers
80 2801 : let marker = SemanticNodeMarkerType(node.node_type().0 as u8);
81 2801 : self.add_constant(state, node_id, inner, marker);
82 2801 : }
83 : // interval '30 days'
84 : NodeType::OBJECT_SQL_CONST_INTERVAL_CAST => {
85 9 : let [value, interval] = attrs!(state.children(node), [K::SQL_CONST_CAST_VALUE, K::SQL_CONST_CAST_INTERVAL]);
86 9 : let Some(value) = value else { continue };
87 9 : let Some(value_expr) = Self::constant_of(state, state.node_id(value)) else { continue };
88 9 : let mut cast = ConstIntervalCast {
89 9 : value_expression_id: value_expr,
90 9 : interval: None,
91 9 : };
92 9 : if let Some(interval) = interval {
93 6 : let [type_attr, precision] =
94 6 : attrs!(state.children(interval), [K::SQL_INTERVAL_TYPE, K::SQL_INTERVAL_PRECISION]);
95 6 : let interval_type =
96 6 : IntervalTypeEnum(type_attr.map(|t| t.children_begin_or_value()).unwrap_or(0) as _);
97 6 : let precision_expression =
98 6 : precision.and_then(|p| state.expression_of(state.node_id(p))).map(|e| e as i32);
99 6 : cast.interval = Some(IntervalType {
100 6 : interval_type,
101 6 : precision_expression,
102 6 : });
103 3 : }
104 9 : self.add_constant(
105 9 : state,
106 9 : node_id,
107 9 : ExpressionInner::ConstIntervalCast(cast),
108 : SemanticNodeMarkerType::CONSTANT_EXPRESSION,
109 : );
110 : }
111 : // N-ary expressions
112 : NodeType::OBJECT_SQL_NARY_EXPRESSION => {
113 910 : let [op_node, args_node] = attrs!(state.children(node), [K::SQL_EXPRESSION_OPERATOR, K::SQL_EXPRESSION_ARGS]);
114 910 : let Some(op_node) = op_node.filter(|n| n.node_type() == NodeType::ENUM_SQL_EXPRESSION_OPERATOR) else {
115 1 : continue;
116 : };
117 : // Are all arguments constant?
118 909 : let args = args_node.map(|a| state.children(a)).unwrap_or(&[]);
119 909 : let mut const_args = Vec::with_capacity(args.len());
120 981 : for arg in args {
121 980 : match Self::constant_of(state, state.node_id(arg)) {
122 127 : Some(e) => const_args.push(e),
123 853 : None => break,
124 : }
125 : }
126 909 : if const_args.len() != args.len() || const_args.len() != 2 {
127 863 : continue;
128 46 : }
129 46 : let op = Op(op_node.children_begin_or_value() as _);
130 46 : let inner = match op {
131 : Op::PLUS | Op::MINUS | Op::MULTIPLY | Op::DIVIDE | Op::MODULUS | Op::XOR | Op::AND | Op::OR => {
132 38 : ExpressionInner::BinaryExpression(BinaryExpression {
133 38 : func: binary_function(op),
134 38 : left_expression_id: const_args[0],
135 38 : right_expression_id: const_args[1],
136 38 : })
137 : }
138 : Op::EQUAL | Op::NOT_EQUAL | Op::LESS_THAN | Op::LESS_EQUAL | Op::GREATER_THAN | Op::GREATER_EQUAL => {
139 8 : ExpressionInner::Comparison(Comparison {
140 8 : func: comparison_function(op),
141 8 : left_expression_id: const_args[0],
142 8 : right_expression_id: const_args[1],
143 8 : })
144 : }
145 0 : _ => continue,
146 : };
147 46 : self.add_constant(state, node_id, inner, SemanticNodeMarkerType::CONSTANT_EXPRESSION);
148 : }
149 : // Function calls
150 : NodeType::OBJECT_SQL_FUNCTION_EXPRESSION => {
151 373 : let Some(expr_id) = state.expression_of(node_id) else { continue };
152 373 : let ExpressionInner::FunctionCall(call) = &state.out.expressions[expr_id as usize].inner else {
153 0 : continue;
154 : };
155 : // Skip functions with any modifiers
156 373 : if call.function_call_modifiers != 0 {
157 202 : continue;
158 171 : }
159 : // FILTER and ORDER BY have no modifier bit, an aggregate with either is not constant
160 171 : let [filter, order] = attrs!(state.children(node), [K::SQL_FUNCTION_FILTER, K::SQL_FUNCTION_ORDER]);
161 171 : if filter.is_some() || order.is_some() {
162 4 : continue;
163 167 : }
164 167 : match call.arguments {
165 : // Constant, but not stored as a root
166 41 : FunctionArguments::None => {
167 41 : state.out.expressions[expr_id as usize].is_constant_expression = true;
168 41 : }
169 126 : FunctionArguments::Generic { begin, count } => {
170 126 : let mut is_constant = true;
171 153 : for i in begin..begin + count {
172 153 : let value_node = state.out.function_arguments[i as usize].value_ast_node_id;
173 153 : match state.expression_of(value_node) {
174 : None => {
175 15 : is_constant = false;
176 15 : break;
177 : }
178 138 : Some(arg_expr) => {
179 138 : state.out.function_arguments[i as usize].expression_id = Some(arg_expr);
180 138 : is_constant &= state.out.expressions[arg_expr as usize].is_constant_expression;
181 138 : }
182 : }
183 : }
184 126 : state.out.expressions[expr_id as usize].is_constant_expression = is_constant;
185 126 : if is_constant {
186 23 : self.constants.push(expr_id);
187 23 : state.mark(node_id, SemanticNodeMarkerType::CONSTANT_EXPRESSION);
188 104 : }
189 : }
190 : }
191 : }
192 74819 : _ => {}
193 : }
194 : }
195 3589 : }
196 :
197 3734 : pub fn finish(&mut self, state: &mut AnalysisState) {
198 4919 : for &expr_id in &self.constants {
199 2879 : let node_id = state.out.expressions[expr_id as usize].ast_node_id;
200 2879 : let node = &state.ast[node_id as usize];
201 : // Only store the roots of constant expression trees
202 2879 : let parent = state.expression_of(node.parent());
203 2879 : if parent.is_some_and(|p| state.out.expressions[p as usize].is_constant_expression) {
204 12 : continue;
205 2867 : }
206 : // If object, mark as constant expression root
207 2867 : if node.node_type().0 >= NodeType::OBJECT_KEYS_.0 {
208 78 : state.mark(node_id, SemanticNodeMarkerType::CONSTANT_EXPRESSION_ROOT);
209 2789 : }
210 2867 : state.out.constant_expressions.push(ConstantExpression { root: expr_id });
211 : }
212 3734 : }
213 : }
|