Skip to main content

dkit_core/query/
parser.rs

1use crate::error::DkitError;
2
3/// 쿼리 AST (Abstract Syntax Tree)
4#[derive(Debug, Clone, PartialEq)]
5pub struct Query {
6    /// 경로 접근 (`.users[0].name`)
7    pub path: Path,
8    /// 파이프라인 연산 (`| where ...`)
9    pub operations: Vec<Operation>,
10}
11
12/// 경로: `.` + 세그먼트들
13#[derive(Debug, Clone, PartialEq)]
14pub struct Path {
15    pub segments: Vec<Segment>,
16}
17
18/// A single segment of a navigation path.
19#[derive(Debug, Clone, PartialEq)]
20#[non_exhaustive]
21pub enum Segment {
22    /// 필드 접근 (`.name`)
23    Field(String),
24    /// 배열 인덱스 접근 (`[0]`, `[-1]`)
25    Index(i64),
26    /// 배열 이터레이션 (`[]`)
27    Iterate,
28    /// 배열 슬라이싱 (`[0:3]`, `[-2:]`, `[::2]`)
29    Slice {
30        start: Option<i64>,
31        end: Option<i64>,
32        step: Option<i64>,
33    },
34    /// 배열 와일드카드 (`[*]`) — Iterate와 동일 의미
35    Wildcard,
36    /// 재귀 하강 (`..key`) — 모든 깊이에서 key 필드를 재귀적으로 수집
37    RecursiveDescent(String),
38}
39
40/// Pipeline operation applied after path navigation (e.g., `| where ...`, `| sort ...`).
41#[derive(Debug, Clone, PartialEq)]
42#[non_exhaustive]
43pub enum Operation {
44    /// `where` 필터링
45    Where(Condition),
46    /// `select` 컬럼 선택: `select name, upper(name), round(price, 2)`
47    Select(Vec<SelectExpr>),
48    /// `sort` 정렬: `sort age` (오름차순) / `sort age desc` (내림차순)
49    Sort { field: String, descending: bool },
50    /// `limit` 결과 제한: `limit 10`
51    Limit(usize),
52    /// `count` 전체 카운트 / `count field` 비null 카운트
53    Count { field: Option<String> },
54    /// `sum field` 숫자 필드 합계
55    Sum { field: String },
56    /// `avg field` 숫자 필드 평균
57    Avg { field: String },
58    /// `min field` 최솟값
59    Min { field: String },
60    /// `max field` 최댓값
61    Max { field: String },
62    /// `distinct field` 고유값 목록
63    Distinct { field: String },
64    /// `median field` 중앙값
65    Median { field: String },
66    /// `percentile field p` p번째 백분위수 (p: 0.0~1.0)
67    Percentile { field: String, p: f64 },
68    /// `stddev field` 표준편차 (모집단)
69    Stddev { field: String },
70    /// `variance field` 분산
71    Variance { field: String },
72    /// `mode field` 최빈값
73    Mode { field: String },
74    /// `group_concat field separator` 그룹 내 문자열 연결
75    GroupConcat { field: String, separator: String },
76    /// `group_by field1, field2` 그룹별 집계
77    /// 집계 연산: `group_by category | select category, count, sum_price`
78    GroupBy {
79        fields: Vec<String>,
80        having: Option<Condition>,
81        aggregates: Vec<GroupAggregate>,
82    },
83    /// 전체 레코드 동일성 기준 중복 제거
84    Unique,
85    /// 특정 필드 기준 중복 제거 (첫 번째 등장 레코드 유지)
86    UniqueBy { field: String },
87    /// 새 필드 추가 (computed column): `--add-field 'total = amount * quantity'`
88    AddField { name: String, expr: Expr },
89    /// 기존 필드 값 변환: `--map 'name = upper(name)'`
90    MapField { name: String, expr: Expr },
91    /// 배열 필드를 개별 행으로 펼침 (unnest/flatten): `--explode tags`
92    Explode { field: String },
93    /// Unpivot (wide → long): 지정 컬럼들을 key-value 쌍으로 변환
94    /// 예: [{name:"a", jan:100, feb:200}] → [{name:"a", variable:"jan", value:100}, {name:"a", variable:"feb", value:200}]
95    Unpivot {
96        /// 피벗 해제할 컬럼명 목록
97        value_columns: Vec<String>,
98        /// 결과에서 컬럼명이 저장될 필드명 (기본: "variable")
99        key_name: String,
100        /// 결과에서 값이 저장될 필드명 (기본: "value")
101        value_name: String,
102    },
103    /// Pivot (long → wide): key-value 쌍을 컬럼으로 변환
104    /// 예: [{name:"a", month:"jan", sales:100}, ...] → [{name:"a", jan:100, feb:200}]
105    Pivot {
106        /// 유지할 인덱스 컬럼명 목록
107        index_fields: Vec<String>,
108        /// 값이 새 컬럼명이 되는 필드
109        columns_field: String,
110        /// 새 컬럼에 채울 값이 있는 필드
111        values_field: String,
112    },
113}
114
115/// GROUP BY 집계 연산 정의
116#[derive(Debug, Clone, PartialEq)]
117pub struct GroupAggregate {
118    pub func: AggregateFunc,
119    pub field: Option<String>,
120    pub alias: String,
121}
122
123/// Aggregate function used in `group_by` operations.
124#[derive(Debug, Clone, PartialEq)]
125#[non_exhaustive]
126pub enum AggregateFunc {
127    Count,
128    Sum,
129    Avg,
130    Min,
131    Max,
132    Median,
133    Percentile(f64),
134    Stddev,
135    Variance,
136    Mode,
137    GroupConcat(String),
138}
139
140/// Boolean condition used in `where` clauses.
141#[derive(Debug, Clone, PartialEq)]
142#[non_exhaustive]
143pub enum Condition {
144    /// 단일 비교: `field op value`
145    Comparison(Comparison),
146    /// 논리 AND: `condition and condition`
147    And(Box<Condition>, Box<Condition>),
148    /// 논리 OR: `condition or condition`
149    Or(Box<Condition>, Box<Condition>),
150}
151
152/// 비교식: `IDENTIFIER compare_op value`
153#[derive(Debug, Clone, PartialEq)]
154pub struct Comparison {
155    pub field: String,
156    pub op: CompareOp,
157    pub value: LiteralValue,
158}
159
160/// Comparison operator used in `where` conditions.
161#[derive(Debug, Clone, PartialEq)]
162#[non_exhaustive]
163pub enum CompareOp {
164    Eq,         // ==
165    Ne,         // !=
166    Gt,         // >
167    Lt,         // <
168    Ge,         // >=
169    Le,         // <=
170    Contains,   // contains
171    StartsWith, // starts_with
172    EndsWith,   // ends_with
173    In,         // in
174    NotIn,      // not in
175    Matches,    // matches (regex)
176    NotMatches, // not matches (regex)
177}
178
179/// Literal value used as a comparison operand or in expressions.
180#[derive(Debug, Clone, PartialEq)]
181#[non_exhaustive]
182pub enum LiteralValue {
183    String(String),
184    Integer(i64),
185    Float(f64),
186    Bool(bool),
187    Null,
188    List(Vec<LiteralValue>),
189}
190
191/// Arithmetic binary operator.
192#[derive(Debug, Clone, PartialEq)]
193pub enum ArithmeticOp {
194    Add, // +
195    Sub, // -
196    Mul, // *
197    Div, // /
198}
199
200/// Expression used in `select` clauses and function arguments.
201#[derive(Debug, Clone, PartialEq)]
202#[non_exhaustive]
203pub enum Expr {
204    /// 필드 참조: `name`
205    Field(String),
206    /// 리터럴 값: `42`, `"hello"`, `true`
207    Literal(LiteralValue),
208    /// 함수 호출: `upper(name)`, `round(price, 2)`, `upper(trim(name))`
209    FuncCall { name: String, args: Vec<Expr> },
210    /// 이항 산술 연산: `amount * quantity`, `first_name + " " + last_name`
211    BinaryOp {
212        op: ArithmeticOp,
213        left: Box<Expr>,
214        right: Box<Expr>,
215    },
216    /// 조건 표현식: `if(condition, then_expr, else_expr)`
217    If {
218        condition: Condition,
219        then_expr: Box<Expr>,
220        else_expr: Box<Expr>,
221    },
222    /// CASE 표현식: `case when cond1 then expr1 [when cond2 then expr2]* [else expr] end`
223    Case {
224        branches: Vec<(Condition, Expr)>,
225        default: Option<Box<Expr>>,
226    },
227    /// 윈도우 함수: `func(...) over (partition by ... order by ...)`
228    Window { func: WindowFunc, over: WindowSpec },
229}
230
231/// 윈도우 함수 종류
232#[derive(Debug, Clone, PartialEq)]
233#[non_exhaustive]
234pub enum WindowFunc {
235    /// `row_number()` — 행 번호 (1부터 시작)
236    RowNumber,
237    /// `rank()` — 순위 (동점 시 같은 순위, 다음 순위 건너뜀)
238    Rank,
239    /// `dense_rank()` — 순위 (동점 시 같은 순위, 다음 순위 연속)
240    DenseRank,
241    /// `lag(expr, offset)` — 이전 행 값 참조
242    Lag { expr: Box<Expr>, offset: usize },
243    /// `lead(expr, offset)` — 다음 행 값 참조
244    Lead { expr: Box<Expr>, offset: usize },
245    /// `first_value(expr)` — 윈도우 내 첫 번째 값
246    FirstValue { expr: Box<Expr> },
247    /// `last_value(expr)` — 윈도우 내 마지막 값
248    LastValue { expr: Box<Expr> },
249    /// 윈도우 집계: `sum(expr) over (...)`
250    Aggregate {
251        func: AggregateFunc,
252        expr: Box<Expr>,
253    },
254}
255
256/// 윈도우 스펙: `OVER (PARTITION BY ... ORDER BY ...)`
257#[derive(Debug, Clone, PartialEq)]
258pub struct WindowSpec {
259    /// `PARTITION BY field1, field2`
260    pub partition_by: Vec<String>,
261    /// `ORDER BY field [ASC|DESC]`
262    pub order_by: Vec<WindowOrderBy>,
263}
264
265/// 윈도우 ORDER BY 절의 개별 항목
266#[derive(Debug, Clone, PartialEq)]
267pub struct WindowOrderBy {
268    pub field: String,
269    pub descending: bool,
270}
271
272/// SELECT 절의 컬럼 표현식
273#[derive(Debug, Clone, PartialEq)]
274pub struct SelectExpr {
275    pub expr: Expr,
276    /// 출력 키 별칭 (`upper(name) as name_upper` 에서 `name_upper`)
277    pub alias: Option<String>,
278}
279
280/// Internal query string parser.
281///
282/// Use the public [`parse_query`] function instead of constructing this directly.
283pub(crate) struct Parser {
284    input: Vec<char>,
285    pos: usize,
286}
287
288impl Parser {
289    pub(crate) fn new(input: &str) -> Self {
290        Self {
291            input: input.chars().collect(),
292            pos: 0,
293        }
294    }
295
296    /// Parse the query string into a [`Query`] AST.
297    pub(crate) fn parse(&mut self) -> Result<Query, DkitError> {
298        self.skip_whitespace();
299        let path = self.parse_path()?;
300        self.skip_whitespace();
301
302        // 파이프라인 연산 파싱: `| where ...`
303        let mut operations = Vec::new();
304        while self.peek() == Some('|') {
305            self.advance(); // consume '|'
306            self.skip_whitespace();
307            operations.push(self.parse_operation()?);
308            self.skip_whitespace();
309        }
310
311        if self.pos != self.input.len() {
312            return Err(DkitError::QueryError(format!(
313                "unexpected character '{}' at position {}",
314                self.input[self.pos], self.pos
315            )));
316        }
317
318        Ok(Query { path, operations })
319    }
320
321    /// 경로를 파싱: `.` 으로 시작
322    fn parse_path(&mut self) -> Result<Path, DkitError> {
323        if !self.consume_char('.') {
324            return Err(DkitError::QueryError(
325                "query must start with '.'".to_string(),
326            ));
327        }
328
329        let mut segments = Vec::new();
330
331        // `.` 만 있으면 루트 경로 (세그먼트 없음)
332        if self.is_at_end() {
333            return Ok(Path { segments });
334        }
335
336        // `..key` — 재귀 하강 (루트 경로 바로 뒤)
337        if self.peek() == Some('.') {
338            self.advance(); // consume the second '.'
339            let field = self.parse_field()?;
340            if let Segment::Field(name) = field {
341                segments.push(Segment::RecursiveDescent(name));
342            }
343            return Ok(Path { segments });
344        }
345
346        // 첫 번째 세그먼트: `[` 이면 인덱스/이터레이터, 아니면 필드
347        // Note: at this point, peek is NOT '.', so it must be '[' or identifier
348        if self.peek() == Some('[') {
349            segments.push(self.parse_bracket()?);
350        } else if self.peek_is_identifier_start() {
351            segments.push(self.parse_field()?);
352        }
353
354        // 나머지 세그먼트
355        while !self.is_at_end() {
356            self.skip_whitespace();
357            if self.peek() == Some('.') {
358                // Check for `..` (recursive descent)
359                if self.pos + 1 < self.input.len() && self.input[self.pos + 1] == '.' {
360                    self.advance(); // consume first '.'
361                    self.advance(); // consume second '.'
362                    let field = self.parse_field()?;
363                    if let Segment::Field(name) = field {
364                        segments.push(Segment::RecursiveDescent(name));
365                    }
366                } else {
367                    self.advance(); // consume '.'
368                    if self.peek() == Some('[') {
369                        segments.push(self.parse_bracket()?);
370                    } else {
371                        segments.push(self.parse_field()?);
372                    }
373                }
374            } else if self.peek() == Some('[') {
375                segments.push(self.parse_bracket()?);
376            } else {
377                break;
378            }
379        }
380
381        Ok(Path { segments })
382    }
383
384    /// 필드 이름 파싱
385    fn parse_field(&mut self) -> Result<Segment, DkitError> {
386        let start = self.pos;
387        while !self.is_at_end() {
388            let c = self.input[self.pos];
389            if c.is_alphanumeric() || c == '_' || c == '-' {
390                self.pos += 1;
391            } else {
392                break;
393            }
394        }
395
396        if self.pos == start {
397            return Err(DkitError::QueryError(format!(
398                "expected field name at position {}",
399                self.pos
400            )));
401        }
402
403        let name: String = self.input[start..self.pos].iter().collect();
404        Ok(Segment::Field(name))
405    }
406
407    /// `[...]` 파싱: 인덱스, 이터레이션, 슬라이스, 와일드카드
408    fn parse_bracket(&mut self) -> Result<Segment, DkitError> {
409        if !self.consume_char('[') {
410            return Err(DkitError::QueryError(format!(
411                "expected '[' at position {}",
412                self.pos
413            )));
414        }
415
416        self.skip_whitespace();
417
418        // `[]` — 이터레이션
419        if self.peek() == Some(']') {
420            self.advance();
421            return Ok(Segment::Iterate);
422        }
423
424        // `[*]` — 와일드카드
425        if self.peek() == Some('*') {
426            self.advance();
427            self.skip_whitespace();
428            if !self.consume_char(']') {
429                return Err(DkitError::QueryError(format!(
430                    "expected ']' after '*' at position {}",
431                    self.pos
432                )));
433            }
434            return Ok(Segment::Wildcard);
435        }
436
437        // `[:]` — 슬라이스 (콜론으로 시작)
438        if self.peek() == Some(':') {
439            return self.parse_slice(None);
440        }
441
442        // 숫자 파싱 (인덱스 또는 슬라이스의 start)
443        let negative = self.consume_char('-');
444        let start = self.pos;
445        while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
446            self.pos += 1;
447        }
448        if self.pos == start {
449            return Err(DkitError::QueryError(format!(
450                "expected integer index at position {}",
451                self.pos
452            )));
453        }
454
455        let num_str: String = self.input[start..self.pos].iter().collect();
456        let num: i64 = num_str.parse().map_err(|_| {
457            DkitError::QueryError(format!("invalid index '{}' at position {}", num_str, start))
458        })?;
459        let num = if negative { -num } else { num };
460
461        self.skip_whitespace();
462
463        // `:` 이 나오면 슬라이스
464        if self.peek() == Some(':') {
465            return self.parse_slice(Some(num));
466        }
467
468        // `]` 이면 단일 인덱스
469        if !self.consume_char(']') {
470            return Err(DkitError::QueryError(format!(
471                "expected ']' or ':' at position {}",
472                self.pos
473            )));
474        }
475
476        Ok(Segment::Index(num))
477    }
478
479    /// 슬라이스 나머지 파싱: start는 이미 파싱됨, `:` 부터 시작
480    fn parse_slice(&mut self, start: Option<i64>) -> Result<Segment, DkitError> {
481        // consume first ':'
482        if !self.consume_char(':') {
483            return Err(DkitError::QueryError(format!(
484                "expected ':' at position {}",
485                self.pos
486            )));
487        }
488
489        self.skip_whitespace();
490
491        // end 파싱
492        let end = if self.peek() == Some(']') || self.peek() == Some(':') {
493            None
494        } else {
495            Some(self.parse_signed_integer()?)
496        };
497
498        self.skip_whitespace();
499
500        // step 파싱 (optional second ':')
501        let step = if self.peek() == Some(':') {
502            self.advance();
503            self.skip_whitespace();
504            if self.peek() == Some(']') {
505                None
506            } else {
507                Some(self.parse_signed_integer()?)
508            }
509        } else {
510            None
511        };
512
513        self.skip_whitespace();
514        if !self.consume_char(']') {
515            return Err(DkitError::QueryError(format!(
516                "expected ']' at position {}",
517                self.pos
518            )));
519        }
520
521        Ok(Segment::Slice { start, end, step })
522    }
523
524    /// 부호 있는 정수 파싱
525    fn parse_signed_integer(&mut self) -> Result<i64, DkitError> {
526        let negative = self.consume_char('-');
527        let start = self.pos;
528        while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
529            self.pos += 1;
530        }
531        if self.pos == start {
532            return Err(DkitError::QueryError(format!(
533                "expected integer at position {}",
534                self.pos
535            )));
536        }
537        let num_str: String = self.input[start..self.pos].iter().collect();
538        let num: i64 = num_str.parse().map_err(|_| {
539            DkitError::QueryError(format!(
540                "invalid integer '{}' at position {}",
541                num_str, start
542            ))
543        })?;
544        Ok(if negative { -num } else { num })
545    }
546
547    // --- 파이프라인 연산 파싱 ---
548
549    /// 연산 파싱: `where ...`, `select ...`
550    fn parse_operation(&mut self) -> Result<Operation, DkitError> {
551        let keyword = self.parse_keyword()?;
552        match keyword.as_str() {
553            "where" => {
554                self.skip_whitespace();
555                let condition = self.parse_condition()?;
556                Ok(Operation::Where(condition))
557            }
558            "select" => {
559                self.skip_whitespace();
560                let exprs = self.parse_select_expr_list()?;
561                Ok(Operation::Select(exprs))
562            }
563            "sort" => {
564                self.skip_whitespace();
565                let field = self.parse_identifier()?;
566                self.skip_whitespace();
567                let descending = self.try_consume_keyword("desc");
568                Ok(Operation::Sort { field, descending })
569            }
570            "limit" => {
571                self.skip_whitespace();
572                let n = self.parse_positive_integer()?;
573                Ok(Operation::Limit(n))
574            }
575            "count" => {
576                self.skip_whitespace();
577                let field = self.try_parse_identifier();
578                Ok(Operation::Count { field })
579            }
580            "sum" => {
581                self.skip_whitespace();
582                let field = self.parse_identifier()?;
583                Ok(Operation::Sum { field })
584            }
585            "avg" => {
586                self.skip_whitespace();
587                let field = self.parse_identifier()?;
588                Ok(Operation::Avg { field })
589            }
590            "min" => {
591                self.skip_whitespace();
592                let field = self.parse_identifier()?;
593                Ok(Operation::Min { field })
594            }
595            "max" => {
596                self.skip_whitespace();
597                let field = self.parse_identifier()?;
598                Ok(Operation::Max { field })
599            }
600            "distinct" => {
601                self.skip_whitespace();
602                let field = self.parse_identifier()?;
603                Ok(Operation::Distinct { field })
604            }
605            "median" => {
606                self.skip_whitespace();
607                let field = self.parse_identifier()?;
608                Ok(Operation::Median { field })
609            }
610            "percentile" => {
611                self.skip_whitespace();
612                let field = self.parse_identifier()?;
613                self.skip_whitespace();
614                let p = self.parse_float_value()?;
615                if !(0.0..=1.0).contains(&p) {
616                    return Err(DkitError::QueryError(
617                        "percentile: p must be between 0.0 and 1.0".to_string(),
618                    ));
619                }
620                Ok(Operation::Percentile { field, p })
621            }
622            "stddev" => {
623                self.skip_whitespace();
624                let field = self.parse_identifier()?;
625                Ok(Operation::Stddev { field })
626            }
627            "variance" => {
628                self.skip_whitespace();
629                let field = self.parse_identifier()?;
630                Ok(Operation::Variance { field })
631            }
632            "mode" => {
633                self.skip_whitespace();
634                let field = self.parse_identifier()?;
635                Ok(Operation::Mode { field })
636            }
637            "group_concat" => {
638                self.skip_whitespace();
639                let field = self.parse_identifier()?;
640                self.skip_whitespace();
641                let separator = if self.peek() == Some('"') {
642                    match self.parse_string_literal()? {
643                        LiteralValue::String(s) => s,
644                        _ => ", ".to_string(),
645                    }
646                } else {
647                    ", ".to_string()
648                };
649                Ok(Operation::GroupConcat { field, separator })
650            }
651            "group_by" => {
652                self.skip_whitespace();
653                let fields = self.parse_identifier_list()?;
654                self.skip_whitespace();
655
656                // Parse optional aggregate functions
657                let aggregates = self.parse_group_aggregates()?;
658
659                // Parse optional HAVING clause
660                let having = if self.try_consume_keyword("having") {
661                    self.skip_whitespace();
662                    Some(self.parse_condition()?)
663                } else {
664                    None
665                };
666
667                Ok(Operation::GroupBy {
668                    fields,
669                    having,
670                    aggregates,
671                })
672            }
673            _ => Err(DkitError::QueryError(format!(
674                "unknown operation '{}' at position {}",
675                keyword,
676                self.pos - keyword.chars().count()
677            ))),
678        }
679    }
680
681    /// GROUP BY 집계 함수 목록 파싱: `count(), sum(field), avg(field), ...`
682    fn parse_group_aggregates(&mut self) -> Result<Vec<GroupAggregate>, DkitError> {
683        let mut aggregates = Vec::new();
684
685        loop {
686            let saved_pos = self.pos;
687            if let Some(agg) = self.try_parse_single_aggregate()? {
688                aggregates.push(agg);
689                self.skip_whitespace();
690                if !self.consume_char(',') {
691                    // No comma, check if next is "having" or end
692                    break;
693                }
694                self.skip_whitespace();
695            } else {
696                self.pos = saved_pos;
697                break;
698            }
699        }
700
701        Ok(aggregates)
702    }
703
704    /// 단일 집계 함수 파싱: `count()`, `sum(field)`, `avg(field)` 등
705    fn try_parse_single_aggregate(&mut self) -> Result<Option<GroupAggregate>, DkitError> {
706        let saved_pos = self.pos;
707
708        // Try to read a keyword
709        let func_name = match self.parse_keyword() {
710            Ok(name) => name,
711            Err(_) => {
712                self.pos = saved_pos;
713                return Ok(None);
714            }
715        };
716
717        // Check if it's a known aggregate function name
718        let is_known = matches!(
719            func_name.as_str(),
720            "count"
721                | "sum"
722                | "avg"
723                | "min"
724                | "max"
725                | "median"
726                | "percentile"
727                | "stddev"
728                | "variance"
729                | "mode"
730                | "group_concat"
731        );
732        if !is_known {
733            self.pos = saved_pos;
734            return Ok(None);
735        }
736
737        self.skip_whitespace();
738
739        // Must have '('
740        if !self.consume_char('(') {
741            self.pos = saved_pos;
742            return Ok(None);
743        }
744
745        self.skip_whitespace();
746
747        // Parse optional field name
748        let field = if self.peek() == Some(')') {
749            None
750        } else {
751            Some(self.parse_identifier()?)
752        };
753
754        self.skip_whitespace();
755
756        // Parse extra arguments for percentile and group_concat
757        let func = match func_name.as_str() {
758            "count" => AggregateFunc::Count,
759            "sum" => AggregateFunc::Sum,
760            "avg" => AggregateFunc::Avg,
761            "min" => AggregateFunc::Min,
762            "max" => AggregateFunc::Max,
763            "median" => AggregateFunc::Median,
764            "percentile" => {
765                if !self.consume_char(',') {
766                    return Err(DkitError::QueryError(format!(
767                        "percentile() requires a second argument (p value) at position {}",
768                        self.pos
769                    )));
770                }
771                self.skip_whitespace();
772                let p = self.parse_float_value()?;
773                if !(0.0..=1.0).contains(&p) {
774                    return Err(DkitError::QueryError(
775                        "percentile: p must be between 0.0 and 1.0".to_string(),
776                    ));
777                }
778                AggregateFunc::Percentile(p)
779            }
780            "stddev" => AggregateFunc::Stddev,
781            "variance" => AggregateFunc::Variance,
782            "mode" => AggregateFunc::Mode,
783            "group_concat" => {
784                let separator = if self.consume_char(',') {
785                    self.skip_whitespace();
786                    match self.parse_string_literal()? {
787                        LiteralValue::String(s) => s,
788                        _ => ", ".to_string(),
789                    }
790                } else {
791                    ", ".to_string()
792                };
793                AggregateFunc::GroupConcat(separator)
794            }
795            _ => unreachable!(),
796        };
797
798        self.skip_whitespace();
799
800        if !self.consume_char(')') {
801            return Err(DkitError::QueryError(format!(
802                "expected ')' at position {}",
803                self.pos
804            )));
805        }
806
807        // Generate alias
808        let alias = match &field {
809            Some(f) => format!("{}_{}", func_name, f),
810            None => func_name.clone(),
811        };
812
813        Ok(Some(GroupAggregate { func, field, alias }))
814    }
815
816    /// SELECT 절의 표현식 목록 파싱: `expr [as alias] ( "," expr [as alias] )*`
817    fn parse_select_expr_list(&mut self) -> Result<Vec<SelectExpr>, DkitError> {
818        let mut exprs = vec![self.parse_select_expr()?];
819        loop {
820            self.skip_whitespace();
821            if self.consume_char(',') {
822                self.skip_whitespace();
823                exprs.push(self.parse_select_expr()?);
824            } else {
825                break;
826            }
827        }
828        Ok(exprs)
829    }
830
831    /// 단일 SELECT 표현식 파싱: `expr [as alias]`
832    fn parse_select_expr(&mut self) -> Result<SelectExpr, DkitError> {
833        let expr = self.parse_expr()?;
834        self.skip_whitespace();
835        // Optional alias: `as alias_name`
836        let alias = {
837            let saved = self.pos;
838            if let Ok(keyword) = self.parse_keyword() {
839                if keyword == "as" {
840                    self.skip_whitespace();
841                    Some(self.parse_identifier()?)
842                } else {
843                    self.pos = saved;
844                    None
845                }
846            } else {
847                self.pos = saved;
848                None
849            }
850        };
851        Ok(SelectExpr { expr, alias })
852    }
853
854    /// 표현식 파싱: 산술 연산자 포함 (우선순위: +- < */)
855    fn parse_expr(&mut self) -> Result<Expr, DkitError> {
856        self.parse_additive_expr()
857    }
858
859    /// 덧셈/뺄셈 수준 표현식: `term (('+' | '-') term)*`
860    fn parse_additive_expr(&mut self) -> Result<Expr, DkitError> {
861        let mut left = self.parse_multiplicative_expr()?;
862
863        loop {
864            self.skip_whitespace();
865            match self.peek() {
866                Some('+') => {
867                    self.advance();
868                    self.skip_whitespace();
869                    let right = self.parse_multiplicative_expr()?;
870                    left = Expr::BinaryOp {
871                        op: ArithmeticOp::Add,
872                        left: Box::new(left),
873                        right: Box::new(right),
874                    };
875                }
876                Some('-') => {
877                    // Distinguish subtraction from negative number literal at end of expression
878                    // Only treat as subtraction if we've already parsed a left operand
879                    self.advance();
880                    self.skip_whitespace();
881                    let right = self.parse_multiplicative_expr()?;
882                    left = Expr::BinaryOp {
883                        op: ArithmeticOp::Sub,
884                        left: Box::new(left),
885                        right: Box::new(right),
886                    };
887                }
888                _ => break,
889            }
890        }
891
892        Ok(left)
893    }
894
895    /// 곱셈/나눗셈 수준 표현식: `atom (('*' | '/') atom)*`
896    fn parse_multiplicative_expr(&mut self) -> Result<Expr, DkitError> {
897        let mut left = self.parse_atom_expr()?;
898
899        loop {
900            self.skip_whitespace();
901            match self.peek() {
902                Some('*') => {
903                    self.advance();
904                    self.skip_whitespace();
905                    let right = self.parse_atom_expr()?;
906                    left = Expr::BinaryOp {
907                        op: ArithmeticOp::Mul,
908                        left: Box::new(left),
909                        right: Box::new(right),
910                    };
911                }
912                Some('/') => {
913                    self.advance();
914                    self.skip_whitespace();
915                    let right = self.parse_atom_expr()?;
916                    left = Expr::BinaryOp {
917                        op: ArithmeticOp::Div,
918                        left: Box::new(left),
919                        right: Box::new(right),
920                    };
921                }
922                _ => break,
923            }
924        }
925
926        Ok(left)
927    }
928
929    /// 원자 표현식: 리터럴, 필드 참조, 함수 호출, 괄호
930    fn parse_atom_expr(&mut self) -> Result<Expr, DkitError> {
931        match self.peek() {
932            Some('(') => {
933                self.advance(); // consume '('
934                self.skip_whitespace();
935                let expr = self.parse_expr()?;
936                self.skip_whitespace();
937                if !self.consume_char(')') {
938                    return Err(DkitError::QueryError(format!(
939                        "expected ')' at position {}",
940                        self.pos
941                    )));
942                }
943                Ok(expr)
944            }
945            Some('"') => {
946                let lit = self.parse_string_literal()?;
947                Ok(Expr::Literal(lit))
948            }
949            Some(c) if c.is_ascii_digit() => {
950                let lit = self.parse_number_literal()?;
951                Ok(Expr::Literal(lit))
952            }
953            Some(c) if c.is_alphabetic() || c == '_' => {
954                let name = self.parse_identifier()?;
955                // Check for bool/null literals
956                match name.as_str() {
957                    "true" => return Ok(Expr::Literal(LiteralValue::Bool(true))),
958                    "false" => return Ok(Expr::Literal(LiteralValue::Bool(false))),
959                    "null" => return Ok(Expr::Literal(LiteralValue::Null)),
960                    // if(condition, then_expr, else_expr)
961                    "if" => return self.parse_if_expr(),
962                    // case when ... then ... [when ... then ...] [else ...] end
963                    "case" => return self.parse_case_expr(),
964                    _ => {}
965                }
966                // Check for function call: name(...)
967                if self.peek() == Some('(') {
968                    self.advance(); // consume '('
969                    self.skip_whitespace();
970                    let mut args = Vec::new();
971                    if self.peek() != Some(')') {
972                        args.push(self.parse_expr()?);
973                        loop {
974                            self.skip_whitespace();
975                            if self.consume_char(',') {
976                                self.skip_whitespace();
977                                args.push(self.parse_expr()?);
978                            } else {
979                                break;
980                            }
981                        }
982                    }
983                    self.skip_whitespace();
984                    if !self.consume_char(')') {
985                        return Err(DkitError::QueryError(format!(
986                            "expected ')' at position {}",
987                            self.pos
988                        )));
989                    }
990                    // Check for window function: `func(...) over (...)`
991                    let saved_over = self.pos;
992                    self.skip_whitespace();
993                    if self.try_consume_keyword("over") {
994                        let window_func = self.parse_window_func(&name, args)?;
995                        let over = self.parse_window_spec()?;
996                        Ok(Expr::Window {
997                            func: window_func,
998                            over,
999                        })
1000                    } else {
1001                        self.pos = saved_over;
1002                        Ok(Expr::FuncCall { name, args })
1003                    }
1004                } else {
1005                    Ok(Expr::Field(name))
1006                }
1007            }
1008            Some(c) => Err(DkitError::QueryError(format!(
1009                "expected expression at position {}, found '{}'",
1010                self.pos, c
1011            ))),
1012            None => Err(DkitError::QueryError(format!(
1013                "expected expression at position {}",
1014                self.pos
1015            ))),
1016        }
1017    }
1018
1019    /// 함수 이름과 인자로부터 WindowFunc를 결정
1020    fn parse_window_func(&self, name: &str, args: Vec<Expr>) -> Result<WindowFunc, DkitError> {
1021        match name {
1022            "row_number" => {
1023                if !args.is_empty() {
1024                    return Err(DkitError::QueryError(
1025                        "row_number() takes no arguments".to_string(),
1026                    ));
1027                }
1028                Ok(WindowFunc::RowNumber)
1029            }
1030            "rank" => {
1031                if !args.is_empty() {
1032                    return Err(DkitError::QueryError(
1033                        "rank() takes no arguments".to_string(),
1034                    ));
1035                }
1036                Ok(WindowFunc::Rank)
1037            }
1038            "dense_rank" => {
1039                if !args.is_empty() {
1040                    return Err(DkitError::QueryError(
1041                        "dense_rank() takes no arguments".to_string(),
1042                    ));
1043                }
1044                Ok(WindowFunc::DenseRank)
1045            }
1046            "lag" => {
1047                if args.is_empty() || args.len() > 2 {
1048                    return Err(DkitError::QueryError(
1049                        "lag() requires 1 or 2 arguments: lag(expr[, offset])".to_string(),
1050                    ));
1051                }
1052                let expr = Box::new(args[0].clone());
1053                let offset = if args.len() == 2 {
1054                    match &args[1] {
1055                        Expr::Literal(LiteralValue::Integer(n)) if *n >= 0 => *n as usize,
1056                        _ => {
1057                            return Err(DkitError::QueryError(
1058                                "lag() offset must be a non-negative integer literal".to_string(),
1059                            ))
1060                        }
1061                    }
1062                } else {
1063                    1
1064                };
1065                Ok(WindowFunc::Lag { expr, offset })
1066            }
1067            "lead" => {
1068                if args.is_empty() || args.len() > 2 {
1069                    return Err(DkitError::QueryError(
1070                        "lead() requires 1 or 2 arguments: lead(expr[, offset])".to_string(),
1071                    ));
1072                }
1073                let expr = Box::new(args[0].clone());
1074                let offset = if args.len() == 2 {
1075                    match &args[1] {
1076                        Expr::Literal(LiteralValue::Integer(n)) if *n >= 0 => *n as usize,
1077                        _ => {
1078                            return Err(DkitError::QueryError(
1079                                "lead() offset must be a non-negative integer literal".to_string(),
1080                            ))
1081                        }
1082                    }
1083                } else {
1084                    1
1085                };
1086                Ok(WindowFunc::Lead { expr, offset })
1087            }
1088            "first_value" => {
1089                if args.len() != 1 {
1090                    return Err(DkitError::QueryError(
1091                        "first_value() requires exactly 1 argument".to_string(),
1092                    ));
1093                }
1094                Ok(WindowFunc::FirstValue {
1095                    expr: Box::new(args[0].clone()),
1096                })
1097            }
1098            "last_value" => {
1099                if args.len() != 1 {
1100                    return Err(DkitError::QueryError(
1101                        "last_value() requires exactly 1 argument".to_string(),
1102                    ));
1103                }
1104                Ok(WindowFunc::LastValue {
1105                    expr: Box::new(args[0].clone()),
1106                })
1107            }
1108            // Window aggregate functions: sum, avg, count, min, max
1109            "sum" | "avg" | "count" | "min" | "max" => {
1110                let agg_func = match name {
1111                    "sum" => AggregateFunc::Sum,
1112                    "avg" => AggregateFunc::Avg,
1113                    "count" => AggregateFunc::Count,
1114                    "min" => AggregateFunc::Min,
1115                    "max" => AggregateFunc::Max,
1116                    _ => unreachable!(),
1117                };
1118                let expr = if args.len() == 1 {
1119                    Box::new(args[0].clone())
1120                } else if args.is_empty() && name == "count" {
1121                    Box::new(Expr::Literal(LiteralValue::Null))
1122                } else {
1123                    return Err(DkitError::QueryError(format!(
1124                        "{}() over requires exactly 1 argument",
1125                        name
1126                    )));
1127                };
1128                Ok(WindowFunc::Aggregate {
1129                    func: agg_func,
1130                    expr,
1131                })
1132            }
1133            _ => Err(DkitError::QueryError(format!(
1134                "unknown window function: {}",
1135                name
1136            ))),
1137        }
1138    }
1139
1140    /// `OVER (PARTITION BY ... ORDER BY ...)` 절 파싱
1141    fn parse_window_spec(&mut self) -> Result<WindowSpec, DkitError> {
1142        self.skip_whitespace();
1143        if !self.consume_char('(') {
1144            return Err(DkitError::QueryError(format!(
1145                "expected '(' after 'over' at position {}",
1146                self.pos
1147            )));
1148        }
1149        self.skip_whitespace();
1150
1151        let mut partition_by = Vec::new();
1152        let mut order_by = Vec::new();
1153
1154        // Parse optional PARTITION BY
1155        if self.try_consume_keyword("partition") {
1156            self.skip_whitespace();
1157            if !self.try_consume_keyword("by") {
1158                return Err(DkitError::QueryError(format!(
1159                    "expected 'by' after 'partition' at position {}",
1160                    self.pos
1161                )));
1162            }
1163            self.skip_whitespace();
1164            partition_by = self.parse_identifier_list()?;
1165            self.skip_whitespace();
1166        }
1167
1168        // Parse optional ORDER BY
1169        if self.try_consume_keyword("order") {
1170            self.skip_whitespace();
1171            if !self.try_consume_keyword("by") {
1172                return Err(DkitError::QueryError(format!(
1173                    "expected 'by' after 'order' at position {}",
1174                    self.pos
1175                )));
1176            }
1177            self.skip_whitespace();
1178            order_by = self.parse_window_order_by_list()?;
1179            self.skip_whitespace();
1180        }
1181
1182        if !self.consume_char(')') {
1183            return Err(DkitError::QueryError(format!(
1184                "expected ')' to close window spec at position {}",
1185                self.pos
1186            )));
1187        }
1188
1189        Ok(WindowSpec {
1190            partition_by,
1191            order_by,
1192        })
1193    }
1194
1195    /// ORDER BY 절의 컬럼 목록 파싱: `field [ASC|DESC] (, field [ASC|DESC])*`
1196    fn parse_window_order_by_list(&mut self) -> Result<Vec<WindowOrderBy>, DkitError> {
1197        let mut items = Vec::new();
1198        let field = self.parse_identifier()?;
1199        self.skip_whitespace();
1200        let descending = self.try_consume_keyword("desc");
1201        if !descending {
1202            self.try_consume_keyword("asc");
1203        }
1204        items.push(WindowOrderBy { field, descending });
1205
1206        loop {
1207            self.skip_whitespace();
1208            if self.consume_char(',') {
1209                self.skip_whitespace();
1210                let field = self.parse_identifier()?;
1211                self.skip_whitespace();
1212                let descending = self.try_consume_keyword("desc");
1213                if !descending {
1214                    self.try_consume_keyword("asc");
1215                }
1216                items.push(WindowOrderBy { field, descending });
1217            } else {
1218                break;
1219            }
1220        }
1221
1222        Ok(items)
1223    }
1224
1225    /// `if(condition, then_expr, else_expr)` 파싱
1226    fn parse_if_expr(&mut self) -> Result<Expr, DkitError> {
1227        self.skip_whitespace();
1228        if !self.consume_char('(') {
1229            return Err(DkitError::QueryError(format!(
1230                "expected '(' after 'if' at position {}",
1231                self.pos
1232            )));
1233        }
1234        self.skip_whitespace();
1235        let condition = self.parse_condition()?;
1236        self.skip_whitespace();
1237        if !self.consume_char(',') {
1238            return Err(DkitError::QueryError(format!(
1239                "expected ',' after condition in if() at position {}",
1240                self.pos
1241            )));
1242        }
1243        self.skip_whitespace();
1244        let then_expr = self.parse_expr()?;
1245        self.skip_whitespace();
1246        if !self.consume_char(',') {
1247            return Err(DkitError::QueryError(format!(
1248                "expected ',' after then-expression in if() at position {}",
1249                self.pos
1250            )));
1251        }
1252        self.skip_whitespace();
1253        let else_expr = self.parse_expr()?;
1254        self.skip_whitespace();
1255        if !self.consume_char(')') {
1256            return Err(DkitError::QueryError(format!(
1257                "expected ')' at position {}",
1258                self.pos
1259            )));
1260        }
1261        Ok(Expr::If {
1262            condition,
1263            then_expr: Box::new(then_expr),
1264            else_expr: Box::new(else_expr),
1265        })
1266    }
1267
1268    /// `case when cond then expr [when cond then expr]* [else expr] end` 파싱
1269    fn parse_case_expr(&mut self) -> Result<Expr, DkitError> {
1270        let mut branches = Vec::new();
1271        let mut default = None;
1272
1273        loop {
1274            self.skip_whitespace();
1275            let saved_pos = self.pos;
1276            let keyword = self.parse_keyword().unwrap_or_default();
1277            match keyword.as_str() {
1278                "when" => {
1279                    self.skip_whitespace();
1280                    let condition = self.parse_condition()?;
1281                    self.skip_whitespace();
1282                    let then_kw = self.parse_keyword()?;
1283                    if then_kw != "then" {
1284                        return Err(DkitError::QueryError(format!(
1285                            "expected 'then' after condition in case expression, found '{}'",
1286                            then_kw
1287                        )));
1288                    }
1289                    self.skip_whitespace();
1290                    let expr = self.parse_expr()?;
1291                    branches.push((condition, expr));
1292                }
1293                "else" => {
1294                    self.skip_whitespace();
1295                    default = Some(Box::new(self.parse_expr()?));
1296                }
1297                "end" => {
1298                    break;
1299                }
1300                _ => {
1301                    self.pos = saved_pos;
1302                    return Err(DkitError::QueryError(format!(
1303                        "expected 'when', 'else', or 'end' in case expression at position {}",
1304                        self.pos
1305                    )));
1306                }
1307            }
1308        }
1309
1310        if branches.is_empty() {
1311            return Err(DkitError::QueryError(
1312                "case expression requires at least one 'when' branch".to_string(),
1313            ));
1314        }
1315
1316        Ok(Expr::Case { branches, default })
1317    }
1318
1319    /// 쉼표로 구분된 식별자 목록 파싱: `IDENTIFIER ( "," IDENTIFIER )*`
1320    fn parse_identifier_list(&mut self) -> Result<Vec<String>, DkitError> {
1321        let mut fields = vec![self.parse_identifier()?];
1322        loop {
1323            self.skip_whitespace();
1324            if self.consume_char(',') {
1325                self.skip_whitespace();
1326                fields.push(self.parse_identifier()?);
1327            } else {
1328                break;
1329            }
1330        }
1331        Ok(fields)
1332    }
1333
1334    /// 키워드 파싱 (알파벳 + 언더스코어)
1335    fn parse_keyword(&mut self) -> Result<String, DkitError> {
1336        let start = self.pos;
1337        while !self.is_at_end() {
1338            let c = self.input[self.pos];
1339            if c.is_alphabetic() || c == '_' {
1340                self.pos += 1;
1341            } else {
1342                break;
1343            }
1344        }
1345        if self.pos == start {
1346            return Err(DkitError::QueryError(format!(
1347                "expected operation keyword at position {}",
1348                self.pos
1349            )));
1350        }
1351        Ok(self.input[start..self.pos].iter().collect())
1352    }
1353
1354    /// 조건식 파싱: `comparison (and|or comparison)*`
1355    fn parse_condition(&mut self) -> Result<Condition, DkitError> {
1356        let mut left = Condition::Comparison(self.parse_comparison()?);
1357
1358        loop {
1359            self.skip_whitespace();
1360            let saved_pos = self.pos;
1361            if let Ok(keyword) = self.parse_keyword() {
1362                match keyword.as_str() {
1363                    "and" => {
1364                        self.skip_whitespace();
1365                        let right = Condition::Comparison(self.parse_comparison()?);
1366                        left = Condition::And(Box::new(left), Box::new(right));
1367                    }
1368                    "or" => {
1369                        self.skip_whitespace();
1370                        let right = Condition::Comparison(self.parse_comparison()?);
1371                        left = Condition::Or(Box::new(left), Box::new(right));
1372                    }
1373                    _ => {
1374                        // Not a logical operator, restore position
1375                        self.pos = saved_pos;
1376                        break;
1377                    }
1378                }
1379            } else {
1380                break;
1381            }
1382        }
1383
1384        Ok(left)
1385    }
1386
1387    /// 비교식 파싱: `IDENTIFIER compare_op literal_value`
1388    /// 또는 `IDENTIFIER in (value1, value2, ...)` / `IDENTIFIER not in (value1, value2, ...)`
1389    fn parse_comparison(&mut self) -> Result<Comparison, DkitError> {
1390        // 필드 이름
1391        let field = self.parse_identifier()?;
1392        self.skip_whitespace();
1393
1394        // Check for `in` / `not in` operators
1395        let saved_pos = self.pos;
1396        if let Ok(keyword) = self.parse_keyword() {
1397            match keyword.as_str() {
1398                "in" => {
1399                    self.skip_whitespace();
1400                    let list = self.parse_literal_list()?;
1401                    return Ok(Comparison {
1402                        field,
1403                        op: CompareOp::In,
1404                        value: LiteralValue::List(list),
1405                    });
1406                }
1407                "not" => {
1408                    self.skip_whitespace();
1409                    let saved_pos2 = self.pos;
1410                    if let Ok(kw2) = self.parse_keyword() {
1411                        if kw2 == "in" {
1412                            self.skip_whitespace();
1413                            let list = self.parse_literal_list()?;
1414                            return Ok(Comparison {
1415                                field,
1416                                op: CompareOp::NotIn,
1417                                value: LiteralValue::List(list),
1418                            });
1419                        } else if kw2 == "matches" {
1420                            self.skip_whitespace();
1421                            let value = self.parse_literal_value()?;
1422                            return Ok(Comparison {
1423                                field,
1424                                op: CompareOp::NotMatches,
1425                                value,
1426                            });
1427                        }
1428                    }
1429                    self.pos = saved_pos2;
1430                    self.pos = saved_pos;
1431                }
1432                _ => {
1433                    self.pos = saved_pos;
1434                }
1435            }
1436        } else {
1437            self.pos = saved_pos;
1438        }
1439
1440        // 비교 연산자
1441        let op = self.parse_compare_op()?;
1442        self.skip_whitespace();
1443
1444        // 리터럴 값
1445        let value = self.parse_literal_value()?;
1446
1447        Ok(Comparison { field, op, value })
1448    }
1449
1450    /// 식별자 파싱 (필드 이름)
1451    fn parse_identifier(&mut self) -> Result<String, DkitError> {
1452        let start = self.pos;
1453        while !self.is_at_end() {
1454            let c = self.input[self.pos];
1455            if c.is_alphanumeric() || c == '_' || c == '-' {
1456                self.pos += 1;
1457            } else {
1458                break;
1459            }
1460        }
1461        if self.pos == start {
1462            return Err(DkitError::QueryError(format!(
1463                "expected field name at position {}",
1464                self.pos
1465            )));
1466        }
1467        Ok(self.input[start..self.pos].iter().collect())
1468    }
1469
1470    /// 비교 연산자 파싱: ==, !=, >=, <=, >, <, contains, starts_with, ends_with
1471    fn parse_compare_op(&mut self) -> Result<CompareOp, DkitError> {
1472        let c1 = self.peek().ok_or_else(|| {
1473            DkitError::QueryError(format!(
1474                "expected comparison operator at position {}",
1475                self.pos
1476            ))
1477        })?;
1478
1479        match c1 {
1480            '=' => {
1481                self.advance();
1482                if self.consume_char('=') {
1483                    Ok(CompareOp::Eq)
1484                } else {
1485                    Err(DkitError::QueryError(format!(
1486                        "expected '==' at position {}",
1487                        self.pos - 1
1488                    )))
1489                }
1490            }
1491            '!' => {
1492                self.advance();
1493                if self.consume_char('=') {
1494                    Ok(CompareOp::Ne)
1495                } else {
1496                    Err(DkitError::QueryError(format!(
1497                        "expected '!=' at position {}",
1498                        self.pos - 1
1499                    )))
1500                }
1501            }
1502            '>' => {
1503                self.advance();
1504                if self.consume_char('=') {
1505                    Ok(CompareOp::Ge)
1506                } else {
1507                    Ok(CompareOp::Gt)
1508                }
1509            }
1510            '<' => {
1511                self.advance();
1512                if self.consume_char('=') {
1513                    Ok(CompareOp::Le)
1514                } else {
1515                    Ok(CompareOp::Lt)
1516                }
1517            }
1518            c if c.is_alphabetic() => {
1519                let saved_pos = self.pos;
1520                let keyword = self.parse_keyword()?;
1521                match keyword.as_str() {
1522                    "contains" => Ok(CompareOp::Contains),
1523                    "starts_with" => Ok(CompareOp::StartsWith),
1524                    "ends_with" => Ok(CompareOp::EndsWith),
1525                    "matches" => Ok(CompareOp::Matches),
1526                    _ => {
1527                        self.pos = saved_pos;
1528                        Err(DkitError::QueryError(format!(
1529                            "expected comparison operator at position {}, found '{}'",
1530                            saved_pos, keyword
1531                        )))
1532                    }
1533                }
1534            }
1535            _ => Err(DkitError::QueryError(format!(
1536                "expected comparison operator at position {}, found '{}'",
1537                self.pos, c1
1538            ))),
1539        }
1540    }
1541
1542    /// 리터럴 값 파싱: 문자열, 숫자, bool, null
1543    fn parse_literal_value(&mut self) -> Result<LiteralValue, DkitError> {
1544        match self.peek() {
1545            Some('"') => self.parse_string_literal(),
1546            Some(c) if c.is_ascii_digit() || c == '-' => self.parse_number_literal(),
1547            Some(c) if c.is_alphabetic() => {
1548                let word = self.parse_keyword()?;
1549                match word.as_str() {
1550                    "true" => Ok(LiteralValue::Bool(true)),
1551                    "false" => Ok(LiteralValue::Bool(false)),
1552                    "null" => Ok(LiteralValue::Null),
1553                    _ => Err(DkitError::QueryError(format!(
1554                        "unexpected value '{}' at position {}",
1555                        word,
1556                        self.pos - word.len()
1557                    ))),
1558                }
1559            }
1560            Some(c) => Err(DkitError::QueryError(format!(
1561                "unexpected character '{}' at position {}",
1562                c, self.pos
1563            ))),
1564            None => Err(DkitError::QueryError(format!(
1565                "expected value at position {}",
1566                self.pos
1567            ))),
1568        }
1569    }
1570
1571    /// 리터럴 리스트 파싱: `(value1, value2, ...)`
1572    fn parse_literal_list(&mut self) -> Result<Vec<LiteralValue>, DkitError> {
1573        if !self.consume_char('(') {
1574            return Err(DkitError::QueryError(format!(
1575                "expected '(' at position {}",
1576                self.pos
1577            )));
1578        }
1579
1580        let mut values = Vec::new();
1581        self.skip_whitespace();
1582
1583        // Handle empty list
1584        if self.peek() == Some(')') {
1585            self.advance();
1586            return Ok(values);
1587        }
1588
1589        // Parse first value
1590        values.push(self.parse_literal_value()?);
1591
1592        loop {
1593            self.skip_whitespace();
1594            if self.consume_char(')') {
1595                break;
1596            }
1597            if !self.consume_char(',') {
1598                return Err(DkitError::QueryError(format!(
1599                    "expected ',' or ')' at position {}",
1600                    self.pos
1601                )));
1602            }
1603            self.skip_whitespace();
1604            values.push(self.parse_literal_value()?);
1605        }
1606
1607        Ok(values)
1608    }
1609
1610    /// 문자열 리터럴 파싱: `"..."`
1611    fn parse_string_literal(&mut self) -> Result<LiteralValue, DkitError> {
1612        if !self.consume_char('"') {
1613            return Err(DkitError::QueryError(format!(
1614                "expected '\"' at position {}",
1615                self.pos
1616            )));
1617        }
1618        let start = self.pos;
1619        while !self.is_at_end() && self.input[self.pos] != '"' {
1620            self.pos += 1;
1621        }
1622        if self.is_at_end() {
1623            return Err(DkitError::QueryError(format!(
1624                "unterminated string starting at position {}",
1625                start - 1
1626            )));
1627        }
1628        let s: String = self.input[start..self.pos].iter().collect();
1629        self.advance(); // consume closing '"'
1630        Ok(LiteralValue::String(s))
1631    }
1632
1633    /// 숫자 리터럴 파싱: 정수 또는 부동소수점
1634    fn parse_number_literal(&mut self) -> Result<LiteralValue, DkitError> {
1635        let start = self.pos;
1636        if self.peek() == Some('-') {
1637            self.advance();
1638        }
1639        while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
1640            self.pos += 1;
1641        }
1642        let mut is_float = false;
1643        if self.peek() == Some('.') {
1644            is_float = true;
1645            self.advance();
1646            while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
1647                self.pos += 1;
1648            }
1649        }
1650        if self.pos == start || (self.pos == start + 1 && self.input[start] == '-') {
1651            return Err(DkitError::QueryError(format!(
1652                "expected number at position {}",
1653                start
1654            )));
1655        }
1656        let num_str: String = self.input[start..self.pos].iter().collect();
1657        if is_float {
1658            let f: f64 = num_str.parse().map_err(|_| {
1659                DkitError::QueryError(format!(
1660                    "invalid number '{}' at position {}",
1661                    num_str, start
1662                ))
1663            })?;
1664            Ok(LiteralValue::Float(f))
1665        } else {
1666            let n: i64 = num_str.parse().map_err(|_| {
1667                DkitError::QueryError(format!(
1668                    "invalid number '{}' at position {}",
1669                    num_str, start
1670                ))
1671            })?;
1672            Ok(LiteralValue::Integer(n))
1673        }
1674    }
1675
1676    // --- 유틸리티 ---
1677
1678    fn peek(&self) -> Option<char> {
1679        self.input.get(self.pos).copied()
1680    }
1681
1682    fn peek_is_identifier_start(&self) -> bool {
1683        self.peek().is_some_and(|c| c.is_alphabetic() || c == '_')
1684    }
1685
1686    fn advance(&mut self) {
1687        self.pos += 1;
1688    }
1689
1690    fn consume_char(&mut self, expected: char) -> bool {
1691        if self.peek() == Some(expected) {
1692            self.advance();
1693            true
1694        } else {
1695            false
1696        }
1697    }
1698
1699    fn skip_whitespace(&mut self) {
1700        while self.peek().is_some_and(|c| c.is_whitespace()) {
1701            self.advance();
1702        }
1703    }
1704
1705    fn is_at_end(&self) -> bool {
1706        self.pos >= self.input.len()
1707    }
1708
1709    /// 식별자를 시도적으로 파싱: 식별자가 없으면 None 반환 (위치 복원)
1710    fn try_parse_identifier(&mut self) -> Option<String> {
1711        if !self.peek_is_identifier_start() {
1712            return None;
1713        }
1714        let saved_pos = self.pos;
1715        match self.parse_identifier() {
1716            Ok(id) => Some(id),
1717            Err(_) => {
1718                self.pos = saved_pos;
1719                None
1720            }
1721        }
1722    }
1723
1724    /// 키워드를 시도적으로 소비: 매치하면 true, 아니면 위치를 복원하고 false
1725    fn try_consume_keyword(&mut self, keyword: &str) -> bool {
1726        let saved_pos = self.pos;
1727        if let Ok(word) = self.parse_keyword() {
1728            if word == keyword {
1729                return true;
1730            }
1731        }
1732        self.pos = saved_pos;
1733        false
1734    }
1735
1736    /// 양의 정수 파싱 (limit 절용)
1737    /// 부동소수점 값 파싱 (0.95 등)
1738    fn parse_float_value(&mut self) -> Result<f64, DkitError> {
1739        let start = self.pos;
1740        if self.peek() == Some('-') {
1741            self.advance();
1742        }
1743        while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
1744            self.pos += 1;
1745        }
1746        if self.peek() == Some('.') {
1747            self.advance();
1748            while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
1749                self.pos += 1;
1750            }
1751        }
1752        if self.pos == start {
1753            return Err(DkitError::QueryError(format!(
1754                "expected number at position {}",
1755                self.pos
1756            )));
1757        }
1758        let num_str: String = self.input[start..self.pos].iter().collect();
1759        num_str.parse().map_err(|_| {
1760            DkitError::QueryError(format!(
1761                "invalid number '{}' at position {}",
1762                num_str, start
1763            ))
1764        })
1765    }
1766
1767    fn parse_positive_integer(&mut self) -> Result<usize, DkitError> {
1768        let start = self.pos;
1769        while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
1770            self.pos += 1;
1771        }
1772        if self.pos == start {
1773            return Err(DkitError::QueryError(format!(
1774                "expected positive integer at position {}",
1775                self.pos
1776            )));
1777        }
1778        let num_str: String = self.input[start..self.pos].iter().collect();
1779        num_str.parse().map_err(|_| {
1780            DkitError::QueryError(format!(
1781                "invalid integer '{}' at position {}",
1782                num_str, start
1783            ))
1784        })
1785    }
1786}
1787
1788/// 편의 함수: 쿼리 문자열 → Query
1789pub fn parse_query(input: &str) -> Result<Query, DkitError> {
1790    Parser::new(input).parse()
1791}
1792
1793/// `--add-field` 표현식 파싱: `"name = expression"`
1794/// 예: "total = amount * quantity", "full_name = first_name + \" \" + last_name"
1795pub fn parse_add_field_expr(input: &str) -> Result<(String, Expr), DkitError> {
1796    let mut parser = Parser::new(input);
1797    parser.skip_whitespace();
1798    let name = parser.parse_identifier().map_err(|_| {
1799        DkitError::QueryError(format!(
1800            "expected field name in --add-field expression: '{input}'"
1801        ))
1802    })?;
1803    parser.skip_whitespace();
1804    if !parser.consume_char('=') {
1805        return Err(DkitError::QueryError(format!(
1806            "expected '=' after field name in --add-field expression: '{input}'"
1807        )));
1808    }
1809    parser.skip_whitespace();
1810    let expr = parser.parse_expr()?;
1811    parser.skip_whitespace();
1812    if parser.pos != parser.input.len() {
1813        return Err(DkitError::QueryError(format!(
1814            "unexpected character '{}' at position {} in --add-field expression",
1815            parser.input[parser.pos], parser.pos
1816        )));
1817    }
1818    Ok((name, expr))
1819}
1820
1821/// where 절의 조건식만 파싱하는 편의 함수
1822/// 예: "age > 30 and city == \"Seoul\""
1823pub fn parse_condition_expr(input: &str) -> Result<Condition, DkitError> {
1824    let mut parser = Parser::new(input);
1825    parser.skip_whitespace();
1826    let condition = parser.parse_condition()?;
1827    parser.skip_whitespace();
1828    if parser.pos != parser.input.len() {
1829        return Err(DkitError::QueryError(format!(
1830            "unexpected character '{}' at position {} in where expression",
1831            parser.input[parser.pos], parser.pos
1832        )));
1833    }
1834    Ok(condition)
1835}
1836
1837#[cfg(test)]
1838mod tests {
1839    use super::*;
1840
1841    // --- 기본 경로 파싱 ---
1842
1843    #[test]
1844    fn test_root_path() {
1845        let q = parse_query(".").unwrap();
1846        assert!(q.path.segments.is_empty());
1847    }
1848
1849    #[test]
1850    fn test_single_field() {
1851        let q = parse_query(".name").unwrap();
1852        assert_eq!(q.path.segments, vec![Segment::Field("name".to_string())]);
1853    }
1854
1855    #[test]
1856    fn test_nested_fields() {
1857        let q = parse_query(".database.host").unwrap();
1858        assert_eq!(
1859            q.path.segments,
1860            vec![
1861                Segment::Field("database".to_string()),
1862                Segment::Field("host".to_string()),
1863            ]
1864        );
1865    }
1866
1867    #[test]
1868    fn test_deeply_nested_fields() {
1869        let q = parse_query(".a.b.c.d").unwrap();
1870        assert_eq!(q.path.segments.len(), 4);
1871        assert_eq!(q.path.segments[3], Segment::Field("d".to_string()));
1872    }
1873
1874    // --- 배열 인덱싱 ---
1875
1876    #[test]
1877    fn test_array_index() {
1878        let q = parse_query(".users[0]").unwrap();
1879        assert_eq!(
1880            q.path.segments,
1881            vec![Segment::Field("users".to_string()), Segment::Index(0),]
1882        );
1883    }
1884
1885    #[test]
1886    fn test_array_negative_index() {
1887        let q = parse_query(".users[-1]").unwrap();
1888        assert_eq!(
1889            q.path.segments,
1890            vec![Segment::Field("users".to_string()), Segment::Index(-1),]
1891        );
1892    }
1893
1894    #[test]
1895    fn test_array_index_with_field_after() {
1896        let q = parse_query(".users[0].name").unwrap();
1897        assert_eq!(
1898            q.path.segments,
1899            vec![
1900                Segment::Field("users".to_string()),
1901                Segment::Index(0),
1902                Segment::Field("name".to_string()),
1903            ]
1904        );
1905    }
1906
1907    #[test]
1908    fn test_large_index() {
1909        let q = parse_query(".items[999]").unwrap();
1910        assert_eq!(
1911            q.path.segments,
1912            vec![Segment::Field("items".to_string()), Segment::Index(999),]
1913        );
1914    }
1915
1916    // --- 배열 이터레이션 ---
1917
1918    #[test]
1919    fn test_array_iterate() {
1920        let q = parse_query(".users[]").unwrap();
1921        assert_eq!(
1922            q.path.segments,
1923            vec![Segment::Field("users".to_string()), Segment::Iterate,]
1924        );
1925    }
1926
1927    #[test]
1928    fn test_array_iterate_with_field() {
1929        let q = parse_query(".users[].name").unwrap();
1930        assert_eq!(
1931            q.path.segments,
1932            vec![
1933                Segment::Field("users".to_string()),
1934                Segment::Iterate,
1935                Segment::Field("name".to_string()),
1936            ]
1937        );
1938    }
1939
1940    #[test]
1941    fn test_array_iterate_nested() {
1942        let q = parse_query(".data[].items[].name").unwrap();
1943        assert_eq!(
1944            q.path.segments,
1945            vec![
1946                Segment::Field("data".to_string()),
1947                Segment::Iterate,
1948                Segment::Field("items".to_string()),
1949                Segment::Iterate,
1950                Segment::Field("name".to_string()),
1951            ]
1952        );
1953    }
1954
1955    // --- 배열 와일드카드 ---
1956
1957    #[test]
1958    fn test_array_wildcard() {
1959        let q = parse_query(".[*]").unwrap();
1960        assert_eq!(q.path.segments, vec![Segment::Wildcard]);
1961    }
1962
1963    #[test]
1964    fn test_array_wildcard_with_field() {
1965        let q = parse_query(".users[*].name").unwrap();
1966        assert_eq!(
1967            q.path.segments,
1968            vec![
1969                Segment::Field("users".to_string()),
1970                Segment::Wildcard,
1971                Segment::Field("name".to_string()),
1972            ]
1973        );
1974    }
1975
1976    // --- 배열 슬라이싱 ---
1977
1978    #[test]
1979    fn test_array_slice_basic() {
1980        let q = parse_query(".[0:3]").unwrap();
1981        assert_eq!(
1982            q.path.segments,
1983            vec![Segment::Slice {
1984                start: Some(0),
1985                end: Some(3),
1986                step: None
1987            }]
1988        );
1989    }
1990
1991    #[test]
1992    fn test_array_slice_open_end() {
1993        let q = parse_query(".[1:]").unwrap();
1994        assert_eq!(
1995            q.path.segments,
1996            vec![Segment::Slice {
1997                start: Some(1),
1998                end: None,
1999                step: None
2000            }]
2001        );
2002    }
2003
2004    #[test]
2005    fn test_array_slice_open_start() {
2006        let q = parse_query(".[:3]").unwrap();
2007        assert_eq!(
2008            q.path.segments,
2009            vec![Segment::Slice {
2010                start: None,
2011                end: Some(3),
2012                step: None
2013            }]
2014        );
2015    }
2016
2017    #[test]
2018    fn test_array_slice_negative() {
2019        let q = parse_query(".[-2:]").unwrap();
2020        assert_eq!(
2021            q.path.segments,
2022            vec![Segment::Slice {
2023                start: Some(-2),
2024                end: None,
2025                step: None
2026            }]
2027        );
2028    }
2029
2030    #[test]
2031    fn test_array_slice_with_step() {
2032        let q = parse_query(".[1:5:2]").unwrap();
2033        assert_eq!(
2034            q.path.segments,
2035            vec![Segment::Slice {
2036                start: Some(1),
2037                end: Some(5),
2038                step: Some(2)
2039            }]
2040        );
2041    }
2042
2043    #[test]
2044    fn test_array_slice_full_open() {
2045        let q = parse_query(".[:]").unwrap();
2046        assert_eq!(
2047            q.path.segments,
2048            vec![Segment::Slice {
2049                start: None,
2050                end: None,
2051                step: None
2052            }]
2053        );
2054    }
2055
2056    #[test]
2057    fn test_array_slice_with_field() {
2058        let q = parse_query(".users[0:3].name").unwrap();
2059        assert_eq!(
2060            q.path.segments,
2061            vec![
2062                Segment::Field("users".to_string()),
2063                Segment::Slice {
2064                    start: Some(0),
2065                    end: Some(3),
2066                    step: None
2067                },
2068                Segment::Field("name".to_string()),
2069            ]
2070        );
2071    }
2072
2073    #[test]
2074    fn test_array_slice_reverse_step() {
2075        let q = parse_query(".[::-1]").unwrap();
2076        assert_eq!(
2077            q.path.segments,
2078            vec![Segment::Slice {
2079                start: None,
2080                end: None,
2081                step: Some(-1)
2082            }]
2083        );
2084    }
2085
2086    // --- 복합 경로 ---
2087
2088    #[test]
2089    fn test_complex_path() {
2090        let q = parse_query(".data.users[0].address.city").unwrap();
2091        assert_eq!(
2092            q.path.segments,
2093            vec![
2094                Segment::Field("data".to_string()),
2095                Segment::Field("users".to_string()),
2096                Segment::Index(0),
2097                Segment::Field("address".to_string()),
2098                Segment::Field("city".to_string()),
2099            ]
2100        );
2101    }
2102
2103    // --- 필드 이름에 특수 문자 ---
2104
2105    #[test]
2106    fn test_field_with_underscore() {
2107        let q = parse_query(".user_name").unwrap();
2108        assert_eq!(
2109            q.path.segments,
2110            vec![Segment::Field("user_name".to_string())]
2111        );
2112    }
2113
2114    #[test]
2115    fn test_field_with_hyphen() {
2116        let q = parse_query(".content-type").unwrap();
2117        assert_eq!(
2118            q.path.segments,
2119            vec![Segment::Field("content-type".to_string())]
2120        );
2121    }
2122
2123    #[test]
2124    fn test_field_with_digits() {
2125        let q = parse_query(".field1").unwrap();
2126        assert_eq!(q.path.segments, vec![Segment::Field("field1".to_string())]);
2127    }
2128
2129    // --- 에러 케이스 ---
2130
2131    #[test]
2132    fn test_error_no_dot() {
2133        let err = parse_query("name").unwrap_err();
2134        assert!(matches!(err, DkitError::QueryError(_)));
2135    }
2136
2137    #[test]
2138    fn test_error_empty() {
2139        let err = parse_query("").unwrap_err();
2140        assert!(matches!(err, DkitError::QueryError(_)));
2141    }
2142
2143    #[test]
2144    fn test_error_unclosed_bracket() {
2145        let err = parse_query(".users[0").unwrap_err();
2146        assert!(matches!(err, DkitError::QueryError(_)));
2147    }
2148
2149    #[test]
2150    fn test_error_invalid_index() {
2151        let err = parse_query(".users[abc]").unwrap_err();
2152        assert!(matches!(err, DkitError::QueryError(_)));
2153    }
2154
2155    #[test]
2156    fn test_error_trailing_garbage() {
2157        let err = parse_query(".name xyz").unwrap_err();
2158        assert!(matches!(err, DkitError::QueryError(_)));
2159    }
2160
2161    // --- 공백 처리 ---
2162
2163    #[test]
2164    fn test_whitespace_around() {
2165        let q = parse_query("  .name  ").unwrap();
2166        assert_eq!(q.path.segments, vec![Segment::Field("name".to_string())]);
2167    }
2168
2169    // --- 루트 배열 접근 ---
2170
2171    #[test]
2172    fn test_root_array_index() {
2173        let q = parse_query(".[0]").unwrap();
2174        assert_eq!(q.path.segments, vec![Segment::Index(0)]);
2175    }
2176
2177    #[test]
2178    fn test_root_array_iterate() {
2179        let q = parse_query(".[]").unwrap();
2180        assert_eq!(q.path.segments, vec![Segment::Iterate]);
2181    }
2182
2183    #[test]
2184    fn test_root_iterate_with_field() {
2185        let q = parse_query(".[].name").unwrap();
2186        assert_eq!(
2187            q.path.segments,
2188            vec![Segment::Iterate, Segment::Field("name".to_string()),]
2189        );
2190    }
2191
2192    // --- where 절 파싱 ---
2193
2194    #[test]
2195    fn test_where_eq_integer() {
2196        let q = parse_query(".users[] | where age == 30").unwrap();
2197        assert_eq!(
2198            q.path.segments,
2199            vec![Segment::Field("users".to_string()), Segment::Iterate]
2200        );
2201        assert_eq!(q.operations.len(), 1);
2202        assert_eq!(
2203            q.operations[0],
2204            Operation::Where(Condition::Comparison(Comparison {
2205                field: "age".to_string(),
2206                op: CompareOp::Eq,
2207                value: LiteralValue::Integer(30),
2208            }))
2209        );
2210    }
2211
2212    #[test]
2213    fn test_where_ne_string() {
2214        let q = parse_query(".items[] | where status != \"inactive\"").unwrap();
2215        assert_eq!(
2216            q.operations[0],
2217            Operation::Where(Condition::Comparison(Comparison {
2218                field: "status".to_string(),
2219                op: CompareOp::Ne,
2220                value: LiteralValue::String("inactive".to_string()),
2221            }))
2222        );
2223    }
2224
2225    #[test]
2226    fn test_where_gt() {
2227        let q = parse_query(".[] | where age > 25").unwrap();
2228        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2229            panic!("expected Comparison");
2230        };
2231        assert_eq!(cmp.field, "age");
2232        assert_eq!(cmp.op, CompareOp::Gt);
2233        assert_eq!(cmp.value, LiteralValue::Integer(25));
2234    }
2235
2236    #[test]
2237    fn test_where_lt() {
2238        let q = parse_query(".[] | where price < 100").unwrap();
2239        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2240            panic!("expected Comparison");
2241        };
2242        assert_eq!(cmp.op, CompareOp::Lt);
2243        assert_eq!(cmp.value, LiteralValue::Integer(100));
2244    }
2245
2246    #[test]
2247    fn test_where_ge() {
2248        let q = parse_query(".[] | where score >= 80").unwrap();
2249        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2250            panic!("expected Comparison");
2251        };
2252        assert_eq!(cmp.op, CompareOp::Ge);
2253        assert_eq!(cmp.value, LiteralValue::Integer(80));
2254    }
2255
2256    #[test]
2257    fn test_where_le() {
2258        let q = parse_query(".[] | where price <= 1000").unwrap();
2259        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2260            panic!("expected Comparison");
2261        };
2262        assert_eq!(cmp.op, CompareOp::Le);
2263        assert_eq!(cmp.value, LiteralValue::Integer(1000));
2264    }
2265
2266    #[test]
2267    fn test_where_float_literal() {
2268        let q = parse_query(".[] | where score > 3.14").unwrap();
2269        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2270            panic!("expected Comparison");
2271        };
2272        assert_eq!(cmp.value, LiteralValue::Float(3.14));
2273    }
2274
2275    #[test]
2276    fn test_where_negative_number() {
2277        let q = parse_query(".[] | where temp > -10").unwrap();
2278        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2279            panic!("expected Comparison");
2280        };
2281        assert_eq!(cmp.value, LiteralValue::Integer(-10));
2282    }
2283
2284    #[test]
2285    fn test_where_bool_literal() {
2286        let q = parse_query(".[] | where active == true").unwrap();
2287        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2288            panic!("expected Comparison");
2289        };
2290        assert_eq!(cmp.value, LiteralValue::Bool(true));
2291    }
2292
2293    #[test]
2294    fn test_where_null_literal() {
2295        let q = parse_query(".[] | where value == null").unwrap();
2296        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2297            panic!("expected Comparison");
2298        };
2299        assert_eq!(cmp.value, LiteralValue::Null);
2300    }
2301
2302    #[test]
2303    fn test_where_no_operations_for_path_only() {
2304        let q = parse_query(".users[0].name").unwrap();
2305        assert!(q.operations.is_empty());
2306    }
2307
2308    #[test]
2309    fn test_where_with_extra_whitespace() {
2310        let q = parse_query(".[]  |  where  age  >  30  ").unwrap();
2311        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2312            panic!("expected Comparison");
2313        };
2314        assert_eq!(cmp.field, "age");
2315        assert_eq!(cmp.op, CompareOp::Gt);
2316        assert_eq!(cmp.value, LiteralValue::Integer(30));
2317    }
2318
2319    // --- where 파싱 에러 ---
2320
2321    #[test]
2322    fn test_error_where_missing_field() {
2323        let err = parse_query(".[] | where == 30").unwrap_err();
2324        assert!(matches!(err, DkitError::QueryError(_)));
2325    }
2326
2327    #[test]
2328    fn test_error_where_missing_operator() {
2329        let err = parse_query(".[] | where age 30").unwrap_err();
2330        assert!(matches!(err, DkitError::QueryError(_)));
2331    }
2332
2333    #[test]
2334    fn test_error_where_missing_value() {
2335        let err = parse_query(".[] | where age >").unwrap_err();
2336        assert!(matches!(err, DkitError::QueryError(_)));
2337    }
2338
2339    #[test]
2340    fn test_error_where_unterminated_string() {
2341        let err = parse_query(".[] | where name == \"hello").unwrap_err();
2342        assert!(matches!(err, DkitError::QueryError(_)));
2343    }
2344
2345    #[test]
2346    fn test_error_unknown_operation() {
2347        let err = parse_query(".[] | foobar age > 30").unwrap_err();
2348        assert!(matches!(err, DkitError::QueryError(_)));
2349    }
2350
2351    // --- 문자열 연산자 파싱 ---
2352
2353    #[test]
2354    fn test_where_contains() {
2355        let q = parse_query(".[] | where email contains \"@gmail\"").unwrap();
2356        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2357            panic!("expected Comparison");
2358        };
2359        assert_eq!(cmp.field, "email");
2360        assert_eq!(cmp.op, CompareOp::Contains);
2361        assert_eq!(cmp.value, LiteralValue::String("@gmail".to_string()));
2362    }
2363
2364    #[test]
2365    fn test_where_starts_with() {
2366        let q = parse_query(".[] | where name starts_with \"A\"").unwrap();
2367        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2368            panic!("expected Comparison");
2369        };
2370        assert_eq!(cmp.field, "name");
2371        assert_eq!(cmp.op, CompareOp::StartsWith);
2372        assert_eq!(cmp.value, LiteralValue::String("A".to_string()));
2373    }
2374
2375    #[test]
2376    fn test_where_ends_with() {
2377        let q = parse_query(".[] | where file ends_with \".json\"").unwrap();
2378        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2379            panic!("expected Comparison");
2380        };
2381        assert_eq!(cmp.field, "file");
2382        assert_eq!(cmp.op, CompareOp::EndsWith);
2383        assert_eq!(cmp.value, LiteralValue::String(".json".to_string()));
2384    }
2385
2386    #[test]
2387    fn test_where_matches() {
2388        let q = parse_query(".[] | where email matches \".*@gmail\\.com$\"").unwrap();
2389        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2390            panic!("expected Comparison");
2391        };
2392        assert_eq!(cmp.field, "email");
2393        assert_eq!(cmp.op, CompareOp::Matches);
2394        assert_eq!(
2395            cmp.value,
2396            LiteralValue::String(".*@gmail\\.com$".to_string())
2397        );
2398    }
2399
2400    #[test]
2401    fn test_where_not_matches() {
2402        let q = parse_query(".[] | where name not matches \"^test_\"").unwrap();
2403        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2404            panic!("expected Comparison");
2405        };
2406        assert_eq!(cmp.field, "name");
2407        assert_eq!(cmp.op, CompareOp::NotMatches);
2408        assert_eq!(cmp.value, LiteralValue::String("^test_".to_string()));
2409    }
2410
2411    // --- 논리 연산자 파싱 ---
2412
2413    #[test]
2414    fn test_where_and() {
2415        let q = parse_query(".[] | where age > 25 and city == \"Seoul\"").unwrap();
2416        let Operation::Where(cond) = &q.operations[0] else {
2417            panic!("expected Where operation");
2418        };
2419        match cond {
2420            Condition::And(left, right) => {
2421                let Condition::Comparison(l) = left.as_ref() else {
2422                    panic!("expected left Comparison");
2423                };
2424                assert_eq!(l.field, "age");
2425                assert_eq!(l.op, CompareOp::Gt);
2426                assert_eq!(l.value, LiteralValue::Integer(25));
2427                let Condition::Comparison(r) = right.as_ref() else {
2428                    panic!("expected right Comparison");
2429                };
2430                assert_eq!(r.field, "city");
2431                assert_eq!(r.op, CompareOp::Eq);
2432                assert_eq!(r.value, LiteralValue::String("Seoul".to_string()));
2433            }
2434            _ => panic!("expected And condition"),
2435        }
2436    }
2437
2438    #[test]
2439    fn test_where_or() {
2440        let q = parse_query(".[] | where role == \"admin\" or role == \"manager\"").unwrap();
2441        let Operation::Where(cond) = &q.operations[0] else {
2442            panic!("expected Where operation");
2443        };
2444        match cond {
2445            Condition::Or(left, right) => {
2446                let Condition::Comparison(l) = left.as_ref() else {
2447                    panic!("expected left Comparison");
2448                };
2449                assert_eq!(l.field, "role");
2450                assert_eq!(l.value, LiteralValue::String("admin".to_string()));
2451                let Condition::Comparison(r) = right.as_ref() else {
2452                    panic!("expected right Comparison");
2453                };
2454                assert_eq!(r.field, "role");
2455                assert_eq!(r.value, LiteralValue::String("manager".to_string()));
2456            }
2457            _ => panic!("expected Or condition"),
2458        }
2459    }
2460
2461    #[test]
2462    fn test_where_and_with_string_op() {
2463        let q = parse_query(".[] | where name starts_with \"A\" and age > 20").unwrap();
2464        let Operation::Where(cond) = &q.operations[0] else {
2465            panic!("expected Where operation");
2466        };
2467        assert!(matches!(cond, Condition::And(_, _)));
2468    }
2469
2470    #[test]
2471    fn test_where_chained_and() {
2472        let q = parse_query(".[] | where a == 1 and b == 2 and c == 3").unwrap();
2473        let Operation::Where(cond) = &q.operations[0] else {
2474            panic!("expected Where operation");
2475        };
2476        // Left-associative: ((a==1 and b==2) and c==3)
2477        match cond {
2478            Condition::And(left, right) => {
2479                assert!(matches!(left.as_ref(), Condition::And(_, _)));
2480                assert!(matches!(right.as_ref(), Condition::Comparison(_)));
2481            }
2482            _ => panic!("expected And condition"),
2483        }
2484    }
2485
2486    // --- select 절 파싱 ---
2487
2488    fn field(name: &str) -> SelectExpr {
2489        SelectExpr {
2490            expr: Expr::Field(name.to_string()),
2491            alias: None,
2492        }
2493    }
2494
2495    fn fields(names: &[&str]) -> Operation {
2496        Operation::Select(names.iter().map(|n| field(n)).collect())
2497    }
2498
2499    #[test]
2500    fn test_select_single_field() {
2501        let q = parse_query(".users[] | select name").unwrap();
2502        assert_eq!(q.operations.len(), 1);
2503        assert_eq!(q.operations[0], fields(&["name"]));
2504    }
2505
2506    #[test]
2507    fn test_select_multiple_fields() {
2508        let q = parse_query(".users[] | select name, email").unwrap();
2509        assert_eq!(q.operations[0], fields(&["name", "email"]));
2510    }
2511
2512    #[test]
2513    fn test_select_three_fields() {
2514        let q = parse_query(".users[] | select name, age, email").unwrap();
2515        assert_eq!(q.operations[0], fields(&["name", "age", "email"]));
2516    }
2517
2518    #[test]
2519    fn test_select_with_extra_whitespace() {
2520        let q = parse_query(".[]  |  select  name ,  email  ").unwrap();
2521        assert_eq!(q.operations[0], fields(&["name", "email"]));
2522    }
2523
2524    #[test]
2525    fn test_select_field_with_underscore() {
2526        let q = parse_query(".[] | select user_name, created_at").unwrap();
2527        assert_eq!(q.operations[0], fields(&["user_name", "created_at"]));
2528    }
2529
2530    #[test]
2531    fn test_select_field_with_hyphen() {
2532        let q = parse_query(".[] | select content-type").unwrap();
2533        assert_eq!(q.operations[0], fields(&["content-type"]));
2534    }
2535
2536    #[test]
2537    fn test_where_then_select() {
2538        let q = parse_query(".users[] | where age > 30 | select name, email").unwrap();
2539        assert_eq!(q.operations.len(), 2);
2540        assert!(matches!(&q.operations[0], Operation::Where(_)));
2541        assert_eq!(q.operations[1], fields(&["name", "email"]));
2542    }
2543
2544    #[test]
2545    fn test_error_select_missing_fields() {
2546        let err = parse_query(".[] | select").unwrap_err();
2547        assert!(matches!(err, DkitError::QueryError(_)));
2548    }
2549
2550    #[test]
2551    fn test_select_func_single() {
2552        let q = parse_query(".[] | select upper(name)").unwrap();
2553        assert_eq!(
2554            q.operations[0],
2555            Operation::Select(vec![SelectExpr {
2556                expr: Expr::FuncCall {
2557                    name: "upper".to_string(),
2558                    args: vec![Expr::Field("name".to_string())],
2559                },
2560                alias: None,
2561            }])
2562        );
2563    }
2564
2565    #[test]
2566    fn test_select_func_with_alias() {
2567        let q = parse_query(".[] | select upper(name) as NAME").unwrap();
2568        assert_eq!(
2569            q.operations[0],
2570            Operation::Select(vec![SelectExpr {
2571                expr: Expr::FuncCall {
2572                    name: "upper".to_string(),
2573                    args: vec![Expr::Field("name".to_string())],
2574                },
2575                alias: Some("NAME".to_string()),
2576            }])
2577        );
2578    }
2579
2580    #[test]
2581    fn test_select_func_nested() {
2582        let q = parse_query(".[] | select upper(trim(name))").unwrap();
2583        assert_eq!(
2584            q.operations[0],
2585            Operation::Select(vec![SelectExpr {
2586                expr: Expr::FuncCall {
2587                    name: "upper".to_string(),
2588                    args: vec![Expr::FuncCall {
2589                        name: "trim".to_string(),
2590                        args: vec![Expr::Field("name".to_string())],
2591                    }],
2592                },
2593                alias: None,
2594            }])
2595        );
2596    }
2597
2598    #[test]
2599    fn test_select_func_with_literal_arg() {
2600        let q = parse_query(".[] | select round(price, 2)").unwrap();
2601        assert_eq!(
2602            q.operations[0],
2603            Operation::Select(vec![SelectExpr {
2604                expr: Expr::FuncCall {
2605                    name: "round".to_string(),
2606                    args: vec![
2607                        Expr::Field("price".to_string()),
2608                        Expr::Literal(LiteralValue::Integer(2)),
2609                    ],
2610                },
2611                alias: None,
2612            }])
2613        );
2614    }
2615
2616    #[test]
2617    fn test_select_mixed_fields_and_funcs() {
2618        let q = parse_query(".[] | select name, upper(city)").unwrap();
2619        assert_eq!(
2620            q.operations[0],
2621            Operation::Select(vec![
2622                field("name"),
2623                SelectExpr {
2624                    expr: Expr::FuncCall {
2625                        name: "upper".to_string(),
2626                        args: vec![Expr::Field("city".to_string())],
2627                    },
2628                    alias: None,
2629                }
2630            ])
2631        );
2632    }
2633
2634    // --- sort 절 파싱 ---
2635
2636    #[test]
2637    fn test_sort_asc() {
2638        let q = parse_query(".users[] | sort age").unwrap();
2639        assert_eq!(q.operations.len(), 1);
2640        assert_eq!(
2641            q.operations[0],
2642            Operation::Sort {
2643                field: "age".to_string(),
2644                descending: false,
2645            }
2646        );
2647    }
2648
2649    #[test]
2650    fn test_sort_desc() {
2651        let q = parse_query(".users[] | sort age desc").unwrap();
2652        assert_eq!(q.operations.len(), 1);
2653        assert_eq!(
2654            q.operations[0],
2655            Operation::Sort {
2656                field: "age".to_string(),
2657                descending: true,
2658            }
2659        );
2660    }
2661
2662    #[test]
2663    fn test_sort_with_extra_whitespace() {
2664        let q = parse_query(".[]  |  sort  name  ").unwrap();
2665        assert_eq!(
2666            q.operations[0],
2667            Operation::Sort {
2668                field: "name".to_string(),
2669                descending: false,
2670            }
2671        );
2672    }
2673
2674    #[test]
2675    fn test_sort_desc_with_extra_whitespace() {
2676        let q = parse_query(".[]  |  sort  name  desc  ").unwrap();
2677        assert_eq!(
2678            q.operations[0],
2679            Operation::Sort {
2680                field: "name".to_string(),
2681                descending: true,
2682            }
2683        );
2684    }
2685
2686    #[test]
2687    fn test_sort_field_with_underscore() {
2688        let q = parse_query(".[] | sort created_at").unwrap();
2689        assert_eq!(
2690            q.operations[0],
2691            Operation::Sort {
2692                field: "created_at".to_string(),
2693                descending: false,
2694            }
2695        );
2696    }
2697
2698    #[test]
2699    fn test_error_sort_missing_field() {
2700        let err = parse_query(".[] | sort").unwrap_err();
2701        assert!(matches!(err, DkitError::QueryError(_)));
2702    }
2703
2704    // --- limit 절 파싱 ---
2705
2706    #[test]
2707    fn test_limit() {
2708        let q = parse_query(".users[] | limit 10").unwrap();
2709        assert_eq!(q.operations.len(), 1);
2710        assert_eq!(q.operations[0], Operation::Limit(10));
2711    }
2712
2713    #[test]
2714    fn test_limit_one() {
2715        let q = parse_query(".[] | limit 1").unwrap();
2716        assert_eq!(q.operations[0], Operation::Limit(1));
2717    }
2718
2719    #[test]
2720    fn test_limit_with_extra_whitespace() {
2721        let q = parse_query(".[]  |  limit  5  ").unwrap();
2722        assert_eq!(q.operations[0], Operation::Limit(5));
2723    }
2724
2725    #[test]
2726    fn test_error_limit_missing_number() {
2727        let err = parse_query(".[] | limit").unwrap_err();
2728        assert!(matches!(err, DkitError::QueryError(_)));
2729    }
2730
2731    #[test]
2732    fn test_error_limit_negative() {
2733        let err = parse_query(".[] | limit -5").unwrap_err();
2734        assert!(matches!(err, DkitError::QueryError(_)));
2735    }
2736
2737    // --- 복합 파이프라인 ---
2738
2739    #[test]
2740    fn test_where_sort_limit() {
2741        let q = parse_query(".users[] | where age > 20 | sort age desc | limit 5").unwrap();
2742        assert_eq!(q.operations.len(), 3);
2743        assert!(matches!(&q.operations[0], Operation::Where(_)));
2744        assert_eq!(
2745            q.operations[1],
2746            Operation::Sort {
2747                field: "age".to_string(),
2748                descending: true,
2749            }
2750        );
2751        assert_eq!(q.operations[2], Operation::Limit(5));
2752    }
2753
2754    #[test]
2755    fn test_where_select_sort() {
2756        let q = parse_query(".users[] | where age > 30 | select name, email | sort name").unwrap();
2757        assert_eq!(q.operations.len(), 3);
2758        assert!(matches!(&q.operations[0], Operation::Where(_)));
2759        assert_eq!(q.operations[1], fields(&["name", "email"]));
2760        assert_eq!(
2761            q.operations[2],
2762            Operation::Sort {
2763                field: "name".to_string(),
2764                descending: false,
2765            }
2766        );
2767    }
2768
2769    #[test]
2770    fn test_group_by_single_field() {
2771        let q = parse_query(".[] | group_by category").unwrap();
2772        assert_eq!(q.operations.len(), 1);
2773        match &q.operations[0] {
2774            Operation::GroupBy {
2775                fields,
2776                having,
2777                aggregates,
2778            } => {
2779                assert_eq!(fields, &vec!["category".to_string()]);
2780                assert!(having.is_none());
2781                assert!(aggregates.is_empty());
2782            }
2783            _ => panic!("expected GroupBy"),
2784        }
2785    }
2786
2787    #[test]
2788    fn test_group_by_multiple_fields() {
2789        let q = parse_query(".[] | group_by region, category").unwrap();
2790        match &q.operations[0] {
2791            Operation::GroupBy { fields, .. } => {
2792                assert_eq!(fields, &vec!["region".to_string(), "category".to_string()]);
2793            }
2794            _ => panic!("expected GroupBy"),
2795        }
2796    }
2797
2798    #[test]
2799    fn test_group_by_with_aggregates() {
2800        let q = parse_query(".[] | group_by category count(), sum(price), avg(score)").unwrap();
2801        match &q.operations[0] {
2802            Operation::GroupBy { aggregates, .. } => {
2803                assert_eq!(aggregates.len(), 3);
2804                assert_eq!(aggregates[0].func, AggregateFunc::Count);
2805                assert_eq!(aggregates[0].field, None);
2806                assert_eq!(aggregates[0].alias, "count");
2807                assert_eq!(aggregates[1].func, AggregateFunc::Sum);
2808                assert_eq!(aggregates[1].field, Some("price".to_string()));
2809                assert_eq!(aggregates[1].alias, "sum_price");
2810                assert_eq!(aggregates[2].func, AggregateFunc::Avg);
2811                assert_eq!(aggregates[2].field, Some("score".to_string()));
2812                assert_eq!(aggregates[2].alias, "avg_score");
2813            }
2814            _ => panic!("expected GroupBy"),
2815        }
2816    }
2817
2818    #[test]
2819    fn test_group_by_with_having() {
2820        let q = parse_query(".[] | group_by category count() having count > 5").unwrap();
2821        match &q.operations[0] {
2822            Operation::GroupBy {
2823                fields,
2824                having,
2825                aggregates,
2826            } => {
2827                assert_eq!(fields, &vec!["category".to_string()]);
2828                assert!(having.is_some());
2829                assert_eq!(aggregates.len(), 1);
2830            }
2831            _ => panic!("expected GroupBy"),
2832        }
2833    }
2834
2835    #[test]
2836    fn test_group_by_with_min_max() {
2837        let q = parse_query(".[] | group_by category min(price), max(price)").unwrap();
2838        match &q.operations[0] {
2839            Operation::GroupBy { aggregates, .. } => {
2840                assert_eq!(aggregates.len(), 2);
2841                assert_eq!(aggregates[0].func, AggregateFunc::Min);
2842                assert_eq!(aggregates[1].func, AggregateFunc::Max);
2843            }
2844            _ => panic!("expected GroupBy"),
2845        }
2846    }
2847
2848    #[test]
2849    fn test_group_by_pipeline() {
2850        let q = parse_query(".[] | group_by category count() | sort count desc | limit 5").unwrap();
2851        assert_eq!(q.operations.len(), 3);
2852        assert!(matches!(&q.operations[0], Operation::GroupBy { .. }));
2853        assert!(matches!(
2854            &q.operations[1],
2855            Operation::Sort {
2856                descending: true,
2857                ..
2858            }
2859        ));
2860        assert_eq!(q.operations[2], Operation::Limit(5));
2861    }
2862
2863    // --- parse_add_field_expr tests ---
2864
2865    #[test]
2866    fn test_add_field_simple_arithmetic() {
2867        let (name, expr) = parse_add_field_expr("total = amount * quantity").unwrap();
2868        assert_eq!(name, "total");
2869        assert!(matches!(
2870            expr,
2871            Expr::BinaryOp {
2872                op: ArithmeticOp::Mul,
2873                ..
2874            }
2875        ));
2876    }
2877
2878    #[test]
2879    fn test_add_field_string_concat() {
2880        let (name, expr) =
2881            parse_add_field_expr("full_name = first_name + \" \" + last_name").unwrap();
2882        assert_eq!(name, "full_name");
2883        // Should be (first_name + " ") + last_name
2884        assert!(matches!(
2885            expr,
2886            Expr::BinaryOp {
2887                op: ArithmeticOp::Add,
2888                ..
2889            }
2890        ));
2891    }
2892
2893    #[test]
2894    fn test_add_field_with_literal() {
2895        let (name, expr) = parse_add_field_expr("tax = price * 0.1").unwrap();
2896        assert_eq!(name, "tax");
2897        assert!(matches!(
2898            expr,
2899            Expr::BinaryOp {
2900                op: ArithmeticOp::Mul,
2901                ..
2902            }
2903        ));
2904    }
2905
2906    #[test]
2907    fn test_add_field_complex_expr() {
2908        let (name, expr) = parse_add_field_expr("total = price + price * 0.1").unwrap();
2909        assert_eq!(name, "total");
2910        // price + (price * 0.1) due to precedence
2911        if let Expr::BinaryOp { op, left, right } = &expr {
2912            assert_eq!(*op, ArithmeticOp::Add);
2913            assert!(matches!(left.as_ref(), Expr::Field(f) if f == "price"));
2914            assert!(matches!(
2915                right.as_ref(),
2916                Expr::BinaryOp {
2917                    op: ArithmeticOp::Mul,
2918                    ..
2919                }
2920            ));
2921        } else {
2922            panic!("expected BinaryOp");
2923        }
2924    }
2925
2926    #[test]
2927    fn test_add_field_with_parens() {
2928        let (name, expr) = parse_add_field_expr("total = (price + tax) * quantity").unwrap();
2929        assert_eq!(name, "total");
2930        if let Expr::BinaryOp { op, left, right } = &expr {
2931            assert_eq!(*op, ArithmeticOp::Mul);
2932            assert!(matches!(
2933                left.as_ref(),
2934                Expr::BinaryOp {
2935                    op: ArithmeticOp::Add,
2936                    ..
2937                }
2938            ));
2939            assert!(matches!(right.as_ref(), Expr::Field(f) if f == "quantity"));
2940        } else {
2941            panic!("expected BinaryOp");
2942        }
2943    }
2944
2945    #[test]
2946    fn test_add_field_with_function() {
2947        let (name, expr) = parse_add_field_expr("name_upper = upper(name)").unwrap();
2948        assert_eq!(name, "name_upper");
2949        assert!(matches!(expr, Expr::FuncCall { .. }));
2950    }
2951
2952    #[test]
2953    fn test_add_field_missing_equals() {
2954        let result = parse_add_field_expr("total amount * quantity");
2955        assert!(result.is_err());
2956    }
2957
2958    // --- Expr arithmetic tests ---
2959
2960    #[test]
2961    fn test_expr_division() {
2962        let q = parse_query(".items[] | select total / count as avg").unwrap();
2963        assert_eq!(q.operations.len(), 1);
2964    }
2965
2966    #[test]
2967    fn test_expr_subtraction() {
2968        let q = parse_query(".items[] | select price - discount as net").unwrap();
2969        assert_eq!(q.operations.len(), 1);
2970    }
2971
2972    // --- Recursive descent (`..`) tests ---
2973
2974    #[test]
2975    fn test_recursive_descent_root() {
2976        let q = parse_query("..name").unwrap();
2977        assert_eq!(
2978            q.path.segments,
2979            vec![Segment::RecursiveDescent("name".to_string())]
2980        );
2981        assert!(q.operations.is_empty());
2982    }
2983
2984    #[test]
2985    fn test_recursive_descent_after_field() {
2986        let q = parse_query(".config..host").unwrap();
2987        assert_eq!(
2988            q.path.segments,
2989            vec![
2990                Segment::Field("config".to_string()),
2991                Segment::RecursiveDescent("host".to_string()),
2992            ]
2993        );
2994    }
2995
2996    #[test]
2997    fn test_recursive_descent_with_pipeline() {
2998        let q = parse_query("..name | where name == \"Alice\"").unwrap();
2999        assert_eq!(
3000            q.path.segments,
3001            vec![Segment::RecursiveDescent("name".to_string())]
3002        );
3003        assert_eq!(q.operations.len(), 1);
3004    }
3005
3006    // --- if() expression tests ---
3007
3008    #[test]
3009    fn test_if_expr_simple() {
3010        let q = parse_query(".items[] | select if(age < 18, \"minor\", \"adult\") as category")
3011            .unwrap();
3012        assert_eq!(q.operations.len(), 1);
3013        if let Operation::Select(exprs) = &q.operations[0] {
3014            assert_eq!(exprs.len(), 1);
3015            assert_eq!(exprs[0].alias, Some("category".to_string()));
3016            assert!(matches!(&exprs[0].expr, Expr::If { .. }));
3017        } else {
3018            panic!("expected Select operation");
3019        }
3020    }
3021
3022    #[test]
3023    fn test_if_expr_nested() {
3024        let q = parse_query(
3025            ".[] | select if(age < 18, \"minor\", if(age < 65, \"adult\", \"senior\")) as cat",
3026        )
3027        .unwrap();
3028        if let Operation::Select(exprs) = &q.operations[0] {
3029            if let Expr::If { else_expr, .. } = &exprs[0].expr {
3030                assert!(matches!(else_expr.as_ref(), Expr::If { .. }));
3031            } else {
3032                panic!("expected If expression");
3033            }
3034        } else {
3035            panic!("expected Select operation");
3036        }
3037    }
3038
3039    #[test]
3040    fn test_if_expr_with_and_condition() {
3041        let q = parse_query(".[] | select if(age > 18 and age < 65, \"adult\", \"other\") as cat")
3042            .unwrap();
3043        if let Operation::Select(exprs) = &q.operations[0] {
3044            if let Expr::If { condition, .. } = &exprs[0].expr {
3045                assert!(matches!(condition, Condition::And(_, _)));
3046            } else {
3047                panic!("expected If expression");
3048            }
3049        } else {
3050            panic!("expected Select operation");
3051        }
3052    }
3053
3054    #[test]
3055    fn test_if_expr_missing_paren() {
3056        let result = parse_query(".[] | select if age < 18, \"minor\", \"adult\"");
3057        assert!(result.is_err());
3058    }
3059
3060    // --- case/when expression tests ---
3061
3062    #[test]
3063    fn test_case_simple() {
3064        let q = parse_query(
3065            ".[] | select case when status == \"active\" then \"A\" else \"I\" end as code",
3066        )
3067        .unwrap();
3068        if let Operation::Select(exprs) = &q.operations[0] {
3069            if let Expr::Case { branches, default } = &exprs[0].expr {
3070                assert_eq!(branches.len(), 1);
3071                assert!(default.is_some());
3072            } else {
3073                panic!("expected Case expression");
3074            }
3075        } else {
3076            panic!("expected Select operation");
3077        }
3078    }
3079
3080    #[test]
3081    fn test_case_multiple_when() {
3082        let q = parse_query(
3083            ".[] | select case when age < 18 then \"minor\" when age < 65 then \"adult\" else \"senior\" end as category",
3084        )
3085        .unwrap();
3086        if let Operation::Select(exprs) = &q.operations[0] {
3087            if let Expr::Case { branches, default } = &exprs[0].expr {
3088                assert_eq!(branches.len(), 2);
3089                assert!(default.is_some());
3090            } else {
3091                panic!("expected Case expression");
3092            }
3093        } else {
3094            panic!("expected Select operation");
3095        }
3096    }
3097
3098    #[test]
3099    fn test_case_no_else() {
3100        let q =
3101            parse_query(".[] | select case when status == \"active\" then \"yes\" end as active")
3102                .unwrap();
3103        if let Operation::Select(exprs) = &q.operations[0] {
3104            if let Expr::Case { branches, default } = &exprs[0].expr {
3105                assert_eq!(branches.len(), 1);
3106                assert!(default.is_none());
3107            } else {
3108                panic!("expected Case expression");
3109            }
3110        } else {
3111            panic!("expected Select operation");
3112        }
3113    }
3114
3115    #[test]
3116    fn test_case_no_when_fails() {
3117        let result = parse_query(".[] | select case else \"x\" end as y");
3118        assert!(result.is_err());
3119    }
3120
3121    // --- if/case in add-field ---
3122
3123    #[test]
3124    fn test_add_field_with_if() {
3125        let (name, expr) =
3126            parse_add_field_expr("tier = if(revenue > 10000, \"gold\", \"silver\")").unwrap();
3127        assert_eq!(name, "tier");
3128        assert!(matches!(expr, Expr::If { .. }));
3129    }
3130
3131    #[test]
3132    fn test_add_field_with_case() {
3133        let (name, expr) = parse_add_field_expr(
3134            "tier = case when revenue > 10000 then \"gold\" when revenue > 5000 then \"silver\" else \"bronze\" end",
3135        )
3136        .unwrap();
3137        assert_eq!(name, "tier");
3138        if let Expr::Case { branches, default } = expr {
3139            assert_eq!(branches.len(), 2);
3140            assert!(default.is_some());
3141        } else {
3142            panic!("expected Case expression");
3143        }
3144    }
3145
3146    // --- 통계 집계 함수 파싱 테스트 ---
3147
3148    #[test]
3149    fn test_parse_median() {
3150        let q = parse_query(".[] | median score").unwrap();
3151        assert!(matches!(&q.operations[0], Operation::Median { field } if field == "score"));
3152    }
3153
3154    #[test]
3155    fn test_parse_percentile() {
3156        let q = parse_query(".[] | percentile score 0.95").unwrap();
3157        if let Operation::Percentile { field, p } = &q.operations[0] {
3158            assert_eq!(field, "score");
3159            assert!((p - 0.95).abs() < f64::EPSILON);
3160        } else {
3161            panic!("expected Percentile operation");
3162        }
3163    }
3164
3165    #[test]
3166    fn test_parse_percentile_invalid_p() {
3167        let result = parse_query(".[] | percentile score 1.5");
3168        assert!(result.is_err());
3169    }
3170
3171    #[test]
3172    fn test_parse_stddev() {
3173        let q = parse_query(".[] | stddev salary").unwrap();
3174        assert!(matches!(&q.operations[0], Operation::Stddev { field } if field == "salary"));
3175    }
3176
3177    #[test]
3178    fn test_parse_variance() {
3179        let q = parse_query(".[] | variance salary").unwrap();
3180        assert!(matches!(&q.operations[0], Operation::Variance { field } if field == "salary"));
3181    }
3182
3183    #[test]
3184    fn test_parse_mode() {
3185        let q = parse_query(".[] | mode category").unwrap();
3186        assert!(matches!(&q.operations[0], Operation::Mode { field } if field == "category"));
3187    }
3188
3189    #[test]
3190    fn test_parse_group_concat() {
3191        let q = parse_query(".[] | group_concat name \", \"").unwrap();
3192        if let Operation::GroupConcat { field, separator } = &q.operations[0] {
3193            assert_eq!(field, "name");
3194            assert_eq!(separator, ", ");
3195        } else {
3196            panic!("expected GroupConcat operation");
3197        }
3198    }
3199
3200    #[test]
3201    fn test_parse_group_concat_default_separator() {
3202        let q = parse_query(".[] | group_concat name").unwrap();
3203        if let Operation::GroupConcat { field, separator } = &q.operations[0] {
3204            assert_eq!(field, "name");
3205            assert_eq!(separator, ", ");
3206        } else {
3207            panic!("expected GroupConcat operation");
3208        }
3209    }
3210
3211    #[test]
3212    fn test_parse_group_by_with_median() {
3213        let q = parse_query(".[] | group_by dept median(score)").unwrap();
3214        if let Operation::GroupBy {
3215            fields, aggregates, ..
3216        } = &q.operations[0]
3217        {
3218            assert_eq!(fields, &["dept"]);
3219            assert_eq!(aggregates.len(), 1);
3220            assert!(matches!(aggregates[0].func, AggregateFunc::Median));
3221            assert_eq!(aggregates[0].field, Some("score".to_string()));
3222            assert_eq!(aggregates[0].alias, "median_score");
3223        } else {
3224            panic!("expected GroupBy operation");
3225        }
3226    }
3227
3228    #[test]
3229    fn test_parse_group_by_with_percentile() {
3230        let q = parse_query(".[] | group_by dept percentile(score, 0.95)").unwrap();
3231        if let Operation::GroupBy { aggregates, .. } = &q.operations[0] {
3232            assert_eq!(aggregates.len(), 1);
3233            if let AggregateFunc::Percentile(p) = aggregates[0].func {
3234                assert!((p - 0.95).abs() < f64::EPSILON);
3235            } else {
3236                panic!("expected Percentile aggregate");
3237            }
3238        } else {
3239            panic!("expected GroupBy operation");
3240        }
3241    }
3242
3243    #[test]
3244    fn test_parse_group_by_with_group_concat() {
3245        let q = parse_query(".[] | group_by dept group_concat(name, \", \")").unwrap();
3246        if let Operation::GroupBy { aggregates, .. } = &q.operations[0] {
3247            assert_eq!(aggregates.len(), 1);
3248            if let AggregateFunc::GroupConcat(sep) = &aggregates[0].func {
3249                assert_eq!(sep, ", ");
3250            } else {
3251                panic!("expected GroupConcat aggregate");
3252            }
3253        } else {
3254            panic!("expected GroupBy operation");
3255        }
3256    }
3257
3258    #[test]
3259    fn test_parse_group_by_mixed_aggregates() {
3260        let q = parse_query(".[] | group_by dept count(), median(score), stddev(score)").unwrap();
3261        if let Operation::GroupBy { aggregates, .. } = &q.operations[0] {
3262            assert_eq!(aggregates.len(), 3);
3263            assert!(matches!(aggregates[0].func, AggregateFunc::Count));
3264            assert!(matches!(aggregates[1].func, AggregateFunc::Median));
3265            assert!(matches!(aggregates[2].func, AggregateFunc::Stddev));
3266        } else {
3267            panic!("expected GroupBy operation");
3268        }
3269    }
3270
3271    // --- 윈도우 함수 파싱 테스트 ---
3272
3273    #[test]
3274    fn test_parse_row_number_over() {
3275        let q =
3276            parse_query(".[] | select row_number() over (order by score desc) as rank").unwrap();
3277        if let Operation::Select(exprs) = &q.operations[0] {
3278            assert_eq!(exprs.len(), 1);
3279            assert_eq!(exprs[0].alias, Some("rank".to_string()));
3280            if let Expr::Window { func, over } = &exprs[0].expr {
3281                assert!(matches!(func, WindowFunc::RowNumber));
3282                assert!(over.partition_by.is_empty());
3283                assert_eq!(over.order_by.len(), 1);
3284                assert_eq!(over.order_by[0].field, "score");
3285                assert!(over.order_by[0].descending);
3286            } else {
3287                panic!("expected Window expression");
3288            }
3289        } else {
3290            panic!("expected Select operation");
3291        }
3292    }
3293
3294    #[test]
3295    fn test_parse_rank_with_partition() {
3296        let q = parse_query(
3297            ".[] | select name, rank() over (partition by dept order by score desc) as dept_rank",
3298        )
3299        .unwrap();
3300        if let Operation::Select(exprs) = &q.operations[0] {
3301            assert_eq!(exprs.len(), 2);
3302            if let Expr::Window { func, over } = &exprs[1].expr {
3303                assert!(matches!(func, WindowFunc::Rank));
3304                assert_eq!(over.partition_by, vec!["dept"]);
3305                assert_eq!(over.order_by[0].field, "score");
3306                assert!(over.order_by[0].descending);
3307            } else {
3308                panic!("expected Window expression");
3309            }
3310        } else {
3311            panic!("expected Select operation");
3312        }
3313    }
3314
3315    #[test]
3316    fn test_parse_dense_rank() {
3317        let q = parse_query(".[] | select dense_rank() over (order by score) as drank").unwrap();
3318        if let Operation::Select(exprs) = &q.operations[0] {
3319            if let Expr::Window { func, over } = &exprs[0].expr {
3320                assert!(matches!(func, WindowFunc::DenseRank));
3321                assert!(!over.order_by[0].descending);
3322            } else {
3323                panic!("expected Window expression");
3324            }
3325        } else {
3326            panic!("expected Select operation");
3327        }
3328    }
3329
3330    #[test]
3331    fn test_parse_lag_default_offset() {
3332        let q = parse_query(".[] | select lag(value) over (order by date) as prev_value").unwrap();
3333        if let Operation::Select(exprs) = &q.operations[0] {
3334            if let Expr::Window { func, .. } = &exprs[0].expr {
3335                if let WindowFunc::Lag { expr, offset } = func {
3336                    assert!(matches!(expr.as_ref(), Expr::Field(f) if f == "value"));
3337                    assert_eq!(*offset, 1);
3338                } else {
3339                    panic!("expected Lag");
3340                }
3341            } else {
3342                panic!("expected Window expression");
3343            }
3344        } else {
3345            panic!("expected Select operation");
3346        }
3347    }
3348
3349    #[test]
3350    fn test_parse_lead_with_offset() {
3351        let q =
3352            parse_query(".[] | select lead(value, 2) over (order by date) as next2_value").unwrap();
3353        if let Operation::Select(exprs) = &q.operations[0] {
3354            if let Expr::Window { func, .. } = &exprs[0].expr {
3355                if let WindowFunc::Lead { offset, .. } = func {
3356                    assert_eq!(*offset, 2);
3357                } else {
3358                    panic!("expected Lead");
3359                }
3360            } else {
3361                panic!("expected Window expression");
3362            }
3363        } else {
3364            panic!("expected Select operation");
3365        }
3366    }
3367
3368    #[test]
3369    fn test_parse_first_value() {
3370        let q =
3371            parse_query(".[] | select first_value(name) over (order by score desc) as top_name")
3372                .unwrap();
3373        if let Operation::Select(exprs) = &q.operations[0] {
3374            if let Expr::Window { func, .. } = &exprs[0].expr {
3375                assert!(matches!(func, WindowFunc::FirstValue { .. }));
3376            } else {
3377                panic!("expected Window expression");
3378            }
3379        } else {
3380            panic!("expected Select operation");
3381        }
3382    }
3383
3384    #[test]
3385    fn test_parse_last_value() {
3386        let q = parse_query(".[] | select last_value(name) over (order by score) as last_name")
3387            .unwrap();
3388        if let Operation::Select(exprs) = &q.operations[0] {
3389            if let Expr::Window { func, .. } = &exprs[0].expr {
3390                assert!(matches!(func, WindowFunc::LastValue { .. }));
3391            } else {
3392                panic!("expected Window expression");
3393            }
3394        } else {
3395            panic!("expected Select operation");
3396        }
3397    }
3398
3399    #[test]
3400    fn test_parse_window_aggregate_sum() {
3401        let q =
3402            parse_query(".[] | select sum(amount) over (order by date) as running_total").unwrap();
3403        if let Operation::Select(exprs) = &q.operations[0] {
3404            if let Expr::Window { func, .. } = &exprs[0].expr {
3405                assert!(matches!(
3406                    func,
3407                    WindowFunc::Aggregate {
3408                        func: AggregateFunc::Sum,
3409                        ..
3410                    }
3411                ));
3412            } else {
3413                panic!("expected Window expression");
3414            }
3415        } else {
3416            panic!("expected Select operation");
3417        }
3418    }
3419
3420    #[test]
3421    fn test_parse_window_empty_over() {
3422        let q = parse_query(".[] | select row_number() over () as rn").unwrap();
3423        if let Operation::Select(exprs) = &q.operations[0] {
3424            if let Expr::Window { func, over } = &exprs[0].expr {
3425                assert!(matches!(func, WindowFunc::RowNumber));
3426                assert!(over.partition_by.is_empty());
3427                assert!(over.order_by.is_empty());
3428            } else {
3429                panic!("expected Window expression");
3430            }
3431        } else {
3432            panic!("expected Select operation");
3433        }
3434    }
3435
3436    #[test]
3437    fn test_parse_window_partition_only() {
3438        let q = parse_query(".[] | select count(score) over (partition by dept) as dept_count")
3439            .unwrap();
3440        if let Operation::Select(exprs) = &q.operations[0] {
3441            if let Expr::Window { over, .. } = &exprs[0].expr {
3442                assert_eq!(over.partition_by, vec!["dept"]);
3443                assert!(over.order_by.is_empty());
3444            } else {
3445                panic!("expected Window expression");
3446            }
3447        } else {
3448            panic!("expected Select operation");
3449        }
3450    }
3451
3452    #[test]
3453    fn test_parse_window_multiple_order_by() {
3454        let q =
3455            parse_query(".[] | select rank() over (order by dept asc, score desc) as r").unwrap();
3456        if let Operation::Select(exprs) = &q.operations[0] {
3457            if let Expr::Window { over, .. } = &exprs[0].expr {
3458                assert_eq!(over.order_by.len(), 2);
3459                assert_eq!(over.order_by[0].field, "dept");
3460                assert!(!over.order_by[0].descending);
3461                assert_eq!(over.order_by[1].field, "score");
3462                assert!(over.order_by[1].descending);
3463            } else {
3464                panic!("expected Window expression");
3465            }
3466        } else {
3467            panic!("expected Select operation");
3468        }
3469    }
3470}