1use serde_json::Value;
52
53use crate::{
54 compiler::{
55 aggregation::OrderDirection,
56 fact_table::FactTableMetadata,
57 window_functions::{
58 FrameBoundary, FrameExclusion, FrameType, PartitionByColumn, WindowFrame,
59 WindowFunctionRequest, WindowFunctionSpec, WindowOrderBy, WindowRequest,
60 WindowSelectColumn,
61 },
62 },
63 db::where_clause::{WhereClause, WhereOperator},
64 error::{FraiseQLError, Result},
65};
66
67pub struct WindowQueryParser;
69
70impl WindowQueryParser {
71 pub fn parse(query_json: &Value, _metadata: &FactTableMetadata) -> Result<WindowRequest> {
82 let table_name = query_json
84 .get("table")
85 .and_then(|v| v.as_str())
86 .ok_or_else(|| FraiseQLError::Validation {
87 message: "Missing 'table' field in window query".to_string(),
88 path: None,
89 })?
90 .to_string();
91
92 let select = if let Some(select_array) = query_json.get("select") {
94 Self::parse_select_columns(select_array)?
95 } else {
96 vec![]
97 };
98
99 let windows = if let Some(windows_array) = query_json.get("windows") {
101 Self::parse_window_functions(windows_array)?
102 } else {
103 vec![]
104 };
105
106 let where_clause = if let Some(where_obj) = query_json.get("where") {
108 Some(Self::parse_where_clause(where_obj)?)
109 } else {
110 None
111 };
112
113 let order_by = if let Some(order_array) = query_json.get("orderBy") {
115 Self::parse_order_by(order_array)?
116 } else {
117 vec![]
118 };
119
120 let limit = query_json
122 .get("limit")
123 .and_then(|v| v.as_u64())
124 .map(|n| u32::try_from(n).unwrap_or(u32::MAX));
125
126 let offset = query_json
127 .get("offset")
128 .and_then(|v| v.as_u64())
129 .map(|n| u32::try_from(n).unwrap_or(u32::MAX));
130
131 Ok(WindowRequest {
132 table_name,
133 select,
134 windows,
135 where_clause,
136 order_by,
137 limit,
138 offset,
139 })
140 }
141
142 fn parse_select_columns(select_array: &Value) -> Result<Vec<WindowSelectColumn>> {
144 let Some(arr) = select_array.as_array() else {
145 return Ok(vec![]);
146 };
147
148 arr.iter().map(Self::parse_single_select_column).collect()
149 }
150
151 fn parse_single_select_column(col: &Value) -> Result<WindowSelectColumn> {
152 let col_type =
153 col.get("type")
154 .and_then(|v| v.as_str())
155 .ok_or_else(|| FraiseQLError::Validation {
156 message: "Missing 'type' in select column".to_string(),
157 path: None,
158 })?;
159
160 let alias = col
161 .get("alias")
162 .and_then(|v| v.as_str())
163 .ok_or_else(|| FraiseQLError::Validation {
164 message: "Missing 'alias' in select column".to_string(),
165 path: None,
166 })?
167 .to_string();
168
169 match col_type {
170 "measure" => {
171 let name = col
172 .get("name")
173 .and_then(|v| v.as_str())
174 .ok_or_else(|| FraiseQLError::Validation {
175 message: "Missing 'name' in measure select column".to_string(),
176 path: None,
177 })?
178 .to_string();
179 Ok(WindowSelectColumn::Measure { name, alias })
180 },
181 "dimension" => {
182 let path = col
183 .get("path")
184 .and_then(|v| v.as_str())
185 .ok_or_else(|| FraiseQLError::Validation {
186 message: "Missing 'path' in dimension select column".to_string(),
187 path: None,
188 })?
189 .to_string();
190 Ok(WindowSelectColumn::Dimension { path, alias })
191 },
192 "filter" => {
193 let name = col
194 .get("name")
195 .and_then(|v| v.as_str())
196 .ok_or_else(|| FraiseQLError::Validation {
197 message: "Missing 'name' in filter select column".to_string(),
198 path: None,
199 })?
200 .to_string();
201 Ok(WindowSelectColumn::Filter { name, alias })
202 },
203 _ => Err(FraiseQLError::Validation {
204 message: format!("Unknown select column type: {col_type}"),
205 path: None,
206 }),
207 }
208 }
209
210 fn parse_window_functions(windows_array: &Value) -> Result<Vec<WindowFunctionRequest>> {
212 let Some(arr) = windows_array.as_array() else {
213 return Ok(vec![]);
214 };
215
216 arr.iter().map(Self::parse_single_window_function).collect()
217 }
218
219 fn parse_single_window_function(window: &Value) -> Result<WindowFunctionRequest> {
220 let function = window
222 .get("function")
223 .ok_or_else(|| FraiseQLError::Validation {
224 message: "Missing 'function' in window definition".to_string(),
225 path: None,
226 })
227 .and_then(Self::parse_function_spec)?;
228
229 let alias = window
231 .get("alias")
232 .and_then(|v| v.as_str())
233 .ok_or_else(|| FraiseQLError::Validation {
234 message: "Missing 'alias' in window definition".to_string(),
235 path: None,
236 })?
237 .to_string();
238
239 let partition_by = if let Some(partition_array) = window.get("partitionBy") {
241 Self::parse_partition_by(partition_array)?
242 } else {
243 vec![]
244 };
245
246 let order_by = if let Some(order_array) = window.get("orderBy") {
248 Self::parse_order_by(order_array)?
249 } else {
250 vec![]
251 };
252
253 let frame = window.get("frame").map(Self::parse_frame).transpose()?;
255
256 Ok(WindowFunctionRequest {
257 function,
258 alias,
259 partition_by,
260 order_by,
261 frame,
262 })
263 }
264
265 fn parse_function_spec(func: &Value) -> Result<WindowFunctionSpec> {
267 let func_type =
268 func.get("type")
269 .and_then(|v| v.as_str())
270 .ok_or_else(|| FraiseQLError::Validation {
271 message: "Missing 'type' in function spec".to_string(),
272 path: None,
273 })?;
274
275 match func_type {
276 "row_number" => Ok(WindowFunctionSpec::RowNumber),
278 "rank" => Ok(WindowFunctionSpec::Rank),
279 "dense_rank" => Ok(WindowFunctionSpec::DenseRank),
280 "ntile" => {
281 let n = u32::try_from(func.get("n").and_then(|v| v.as_u64()).ok_or_else(|| {
282 FraiseQLError::Validation {
283 message: "Missing 'n' in NTILE function".to_string(),
284 path: None,
285 }
286 })?)
287 .unwrap_or(u32::MAX);
288 Ok(WindowFunctionSpec::Ntile { n })
289 },
290 "percent_rank" => Ok(WindowFunctionSpec::PercentRank),
291 "cume_dist" => Ok(WindowFunctionSpec::CumeDist),
292
293 "lag" => {
295 let field = Self::extract_string_field(func, "field")?;
296 let offset =
297 i32::try_from(func.get("offset").and_then(|v| v.as_i64()).unwrap_or(1))
298 .unwrap_or(1);
299 let default = func.get("default").cloned();
300 Ok(WindowFunctionSpec::Lag {
301 field,
302 offset,
303 default,
304 })
305 },
306 "lead" => {
307 let field = Self::extract_string_field(func, "field")?;
308 let offset =
309 i32::try_from(func.get("offset").and_then(|v| v.as_i64()).unwrap_or(1))
310 .unwrap_or(1);
311 let default = func.get("default").cloned();
312 Ok(WindowFunctionSpec::Lead {
313 field,
314 offset,
315 default,
316 })
317 },
318 "first_value" => {
319 let field = Self::extract_string_field(func, "field")?;
320 Ok(WindowFunctionSpec::FirstValue { field })
321 },
322 "last_value" => {
323 let field = Self::extract_string_field(func, "field")?;
324 Ok(WindowFunctionSpec::LastValue { field })
325 },
326 "nth_value" => {
327 let field = Self::extract_string_field(func, "field")?;
328 let n = u32::try_from(func.get("n").and_then(|v| v.as_u64()).ok_or_else(|| {
329 FraiseQLError::Validation {
330 message: "Missing 'n' in NTH_VALUE function".to_string(),
331 path: None,
332 }
333 })?)
334 .unwrap_or(u32::MAX);
335 Ok(WindowFunctionSpec::NthValue { field, n })
336 },
337
338 "running_sum" => {
340 let measure = Self::extract_string_field(func, "measure")?;
341 Ok(WindowFunctionSpec::RunningSum { measure })
342 },
343 "running_avg" => {
344 let measure = Self::extract_string_field(func, "measure")?;
345 Ok(WindowFunctionSpec::RunningAvg { measure })
346 },
347 "running_count" => {
348 if let Some(field) = func.get("field").and_then(|v| v.as_str()) {
349 Ok(WindowFunctionSpec::RunningCountField {
350 field: field.to_string(),
351 })
352 } else {
353 Ok(WindowFunctionSpec::RunningCount)
354 }
355 },
356 "running_min" => {
357 let measure = Self::extract_string_field(func, "measure")?;
358 Ok(WindowFunctionSpec::RunningMin { measure })
359 },
360 "running_max" => {
361 let measure = Self::extract_string_field(func, "measure")?;
362 Ok(WindowFunctionSpec::RunningMax { measure })
363 },
364 "running_stddev" => {
365 let measure = Self::extract_string_field(func, "measure")?;
366 Ok(WindowFunctionSpec::RunningStddev { measure })
367 },
368 "running_variance" => {
369 let measure = Self::extract_string_field(func, "measure")?;
370 Ok(WindowFunctionSpec::RunningVariance { measure })
371 },
372
373 _ => Err(FraiseQLError::Validation {
374 message: format!("Unknown window function type: {func_type}"),
375 path: None,
376 }),
377 }
378 }
379
380 fn extract_string_field(obj: &Value, field_name: &str) -> Result<String> {
382 obj.get(field_name).and_then(|v| v.as_str()).map(String::from).ok_or_else(|| {
383 FraiseQLError::Validation {
384 message: format!("Missing '{field_name}' in function spec"),
385 path: None,
386 }
387 })
388 }
389
390 fn parse_partition_by(partition_array: &Value) -> Result<Vec<PartitionByColumn>> {
392 let Some(arr) = partition_array.as_array() else {
393 return Ok(vec![]);
394 };
395
396 arr.iter()
397 .map(|item| {
398 let col_type = item.get("type").and_then(|v| v.as_str()).ok_or_else(|| {
399 FraiseQLError::Validation {
400 message: "Missing 'type' in partitionBy column".to_string(),
401 path: None,
402 }
403 })?;
404
405 match col_type {
406 "dimension" => {
407 let path = item
408 .get("path")
409 .and_then(|v| v.as_str())
410 .ok_or_else(|| FraiseQLError::Validation {
411 message: "Missing 'path' in dimension partition column".to_string(),
412 path: None,
413 })?
414 .to_string();
415 Ok(PartitionByColumn::Dimension { path })
416 },
417 "filter" => {
418 let name = item
419 .get("name")
420 .and_then(|v| v.as_str())
421 .ok_or_else(|| FraiseQLError::Validation {
422 message: "Missing 'name' in filter partition column".to_string(),
423 path: None,
424 })?
425 .to_string();
426 Ok(PartitionByColumn::Filter { name })
427 },
428 "measure" => {
429 let name = item
430 .get("name")
431 .and_then(|v| v.as_str())
432 .ok_or_else(|| FraiseQLError::Validation {
433 message: "Missing 'name' in measure partition column".to_string(),
434 path: None,
435 })?
436 .to_string();
437 Ok(PartitionByColumn::Measure { name })
438 },
439 _ => Err(FraiseQLError::Validation {
440 message: format!("Unknown partition column type: {col_type}"),
441 path: None,
442 }),
443 }
444 })
445 .collect()
446 }
447
448 fn parse_order_by(order_array: &Value) -> Result<Vec<WindowOrderBy>> {
450 let Some(arr) = order_array.as_array() else {
451 return Ok(vec![]);
452 };
453
454 arr.iter()
455 .map(|item| {
456 let field = item
457 .get("field")
458 .and_then(|v| v.as_str())
459 .ok_or_else(|| FraiseQLError::Validation {
460 message: "Missing 'field' in orderBy".to_string(),
461 path: None,
462 })?
463 .to_string();
464
465 let direction = match item.get("direction").and_then(|v| v.as_str()) {
466 Some("DESC" | "desc") => OrderDirection::Desc,
467 _ => OrderDirection::Asc,
468 };
469
470 Ok(WindowOrderBy { field, direction })
471 })
472 .collect()
473 }
474
475 fn parse_where_clause(where_obj: &Value) -> Result<WhereClause> {
477 let Some(obj) = where_obj.as_object() else {
478 return Ok(WhereClause::And(vec![]));
479 };
480
481 let mut conditions = Vec::new();
482
483 for (key, value) in obj {
484 if let Some((field, operator_str)) = Self::parse_where_field_and_operator(key)? {
487 let operator = WhereOperator::from_str(operator_str)?;
488
489 conditions.push(WhereClause::Field {
490 path: vec![crate::utils::to_snake_case(field)],
494 operator,
495 value: value.clone(),
496 });
497 }
498 }
499
500 Ok(WhereClause::And(conditions))
501 }
502
503 fn parse_where_field_and_operator(key: &str) -> Result<Option<(&str, &str)>> {
505 if let Some(last_underscore) = key.rfind('_') {
506 let field = &key[..last_underscore];
507 let operator = &key[last_underscore + 1..];
508
509 match WhereOperator::from_str(operator) {
510 Ok(_) => Ok(Some((field, operator))),
511 Err(_) => Ok(None),
512 }
513 } else {
514 Ok(None)
515 }
516 }
517
518 fn parse_frame(frame: &Value) -> Result<WindowFrame> {
520 let frame_type = match frame.get("frame_type").and_then(|v| v.as_str()) {
521 Some("ROWS") => FrameType::Rows,
522 Some("RANGE") => FrameType::Range,
523 Some("GROUPS") => FrameType::Groups,
524 _ => {
525 return Err(FraiseQLError::Validation {
526 message: "Invalid or missing 'frame_type' in frame".to_string(),
527 path: None,
528 });
529 },
530 };
531
532 let start = frame
533 .get("start")
534 .ok_or_else(|| FraiseQLError::Validation {
535 message: "Missing 'start' in frame".to_string(),
536 path: None,
537 })
538 .and_then(Self::parse_frame_boundary)?;
539
540 let end = frame
541 .get("end")
542 .ok_or_else(|| FraiseQLError::Validation {
543 message: "Missing 'end' in frame".to_string(),
544 path: None,
545 })
546 .and_then(Self::parse_frame_boundary)?;
547
548 let exclusion = frame.get("exclusion").and_then(|v| v.as_str()).map(|s| match s {
549 "current_row" => FrameExclusion::CurrentRow,
550 "group" => FrameExclusion::Group,
551 "ties" => FrameExclusion::Ties,
552 _ => FrameExclusion::NoOthers,
553 });
554
555 Ok(WindowFrame {
556 frame_type,
557 start,
558 end,
559 exclusion,
560 })
561 }
562
563 fn parse_frame_boundary(boundary: &Value) -> Result<FrameBoundary> {
565 let boundary_type = boundary.get("type").and_then(|v| v.as_str()).ok_or_else(|| {
566 FraiseQLError::Validation {
567 message: "Missing 'type' in frame boundary".to_string(),
568 path: None,
569 }
570 })?;
571
572 match boundary_type {
573 "unbounded_preceding" => Ok(FrameBoundary::UnboundedPreceding),
574 "n_preceding" => {
575 let n =
576 u32::try_from(boundary.get("n").and_then(|v| v.as_u64()).ok_or_else(|| {
577 FraiseQLError::Validation {
578 message: "Missing 'n' in N PRECEDING boundary".to_string(),
579 path: None,
580 }
581 })?)
582 .unwrap_or(u32::MAX);
583 Ok(FrameBoundary::NPreceding { n })
584 },
585 "current_row" => Ok(FrameBoundary::CurrentRow),
586 "n_following" => {
587 let n =
588 u32::try_from(boundary.get("n").and_then(|v| v.as_u64()).ok_or_else(|| {
589 FraiseQLError::Validation {
590 message: "Missing 'n' in N FOLLOWING boundary".to_string(),
591 path: None,
592 }
593 })?)
594 .unwrap_or(u32::MAX);
595 Ok(FrameBoundary::NFollowing { n })
596 },
597 "unbounded_following" => Ok(FrameBoundary::UnboundedFollowing),
598 _ => Err(FraiseQLError::Validation {
599 message: format!("Unknown frame boundary type: {boundary_type}"),
600 path: None,
601 }),
602 }
603 }
604}