sql_cli/sql/generators/
sequence_generators.rs

1use crate::data::datatable::{DataColumn, DataRow, DataTable, DataType, DataValue};
2use crate::sql::generators::TableGenerator;
3use anyhow::{anyhow, Result};
4use std::collections::HashMap;
5use std::sync::Arc;
6
7/// RANGE - Generate numeric sequence
8pub struct Range;
9
10impl TableGenerator for Range {
11    fn name(&self) -> &str {
12        "RANGE"
13    }
14
15    fn columns(&self) -> Vec<DataColumn> {
16        vec![DataColumn {
17            name: "value".to_string(),
18            data_type: DataType::Integer,
19            nullable: false,
20            unique_values: Some(0),
21            null_count: 0,
22            metadata: HashMap::new(),
23            qualified_name: None,
24            source_table: None,
25        }]
26    }
27
28    fn generate(&self, args: Vec<DataValue>) -> Result<Arc<DataTable>> {
29        let (start, end, step) = match args.len() {
30            1 => {
31                // RANGE(n) - from 1 to n
32                let end = match args[0] {
33                    DataValue::Integer(n) => n,
34                    DataValue::Float(f) => f as i64,
35                    _ => return Err(anyhow!("RANGE expects numeric arguments")),
36                };
37                (1, end, 1)
38            }
39            2 => {
40                // RANGE(start, end) - from start to end with step 1
41                let start = match args[0] {
42                    DataValue::Integer(n) => n,
43                    DataValue::Float(f) => f as i64,
44                    _ => return Err(anyhow!("RANGE expects numeric arguments")),
45                };
46                let end = match args[1] {
47                    DataValue::Integer(n) => n,
48                    DataValue::Float(f) => f as i64,
49                    _ => return Err(anyhow!("RANGE expects numeric arguments")),
50                };
51                (start, end, 1)
52            }
53            3 => {
54                // RANGE(start, end, step) - from start to end with given step
55                let start = match args[0] {
56                    DataValue::Integer(n) => n,
57                    DataValue::Float(f) => f as i64,
58                    _ => return Err(anyhow!("RANGE expects numeric arguments")),
59                };
60                let end = match args[1] {
61                    DataValue::Integer(n) => n,
62                    DataValue::Float(f) => f as i64,
63                    _ => return Err(anyhow!("RANGE expects numeric arguments")),
64                };
65                let step = match args[2] {
66                    DataValue::Integer(n) => n,
67                    DataValue::Float(f) => f as i64,
68                    _ => return Err(anyhow!("RANGE expects numeric arguments")),
69                };
70                (start, end, step)
71            }
72            _ => {
73                return Err(anyhow!(
74                    "RANGE expects 1-3 arguments (end), (start, end), or (start, end, step)"
75                ))
76            }
77        };
78
79        if step == 0 {
80            return Err(anyhow!("RANGE step cannot be zero"));
81        }
82
83        if (step > 0 && start > end) || (step < 0 && start < end) {
84            return Err(anyhow!(
85                "RANGE parameters invalid: start={}, end={}, step={}",
86                start,
87                end,
88                step
89            ));
90        }
91
92        let mut table = DataTable::new("range");
93        table.add_column(DataColumn::new("value"));
94
95        let mut current = start;
96        while (step > 0 && current <= end) || (step < 0 && current >= end) {
97            table
98                .add_row(DataRow::new(vec![DataValue::Integer(current)]))
99                .map_err(|e| anyhow!(e))?;
100            current += step;
101        }
102
103        Ok(Arc::new(table))
104    }
105
106    fn description(&self) -> &str {
107        "Generate numeric sequence"
108    }
109
110    fn arg_count(&self) -> usize {
111        3 // Can accept 1, 2, or 3 arguments
112    }
113}
114
115/// SERIES - Generate a series with index (like pandas)
116pub struct Series;
117
118impl TableGenerator for Series {
119    fn name(&self) -> &str {
120        "SERIES"
121    }
122
123    fn columns(&self) -> Vec<DataColumn> {
124        vec![
125            DataColumn {
126                name: "index".to_string(),
127                data_type: DataType::Integer,
128                nullable: false,
129                unique_values: Some(0),
130                null_count: 0,
131                metadata: HashMap::new(),
132                qualified_name: None,
133                source_table: None,
134            },
135            DataColumn {
136                name: "value".to_string(),
137                data_type: DataType::Integer,
138                nullable: false,
139                unique_values: Some(0),
140                null_count: 0,
141                metadata: HashMap::new(),
142                qualified_name: None,
143                source_table: None,
144            },
145        ]
146    }
147
148    fn generate(&self, args: Vec<DataValue>) -> Result<Arc<DataTable>> {
149        if args.is_empty() {
150            return Err(anyhow!("SERIES requires at least 1 argument (count)"));
151        }
152
153        let count = match args[0] {
154            DataValue::Integer(n) => n,
155            DataValue::Float(f) => f as i64,
156            _ => return Err(anyhow!("SERIES count must be numeric")),
157        };
158
159        let start = if args.len() > 1 {
160            match args[1] {
161                DataValue::Integer(n) => n,
162                DataValue::Float(f) => f as i64,
163                _ => 0,
164            }
165        } else {
166            0
167        };
168
169        let mut table = DataTable::new("series");
170        table.add_column(DataColumn::new("index"));
171        table.add_column(DataColumn::new("value"));
172
173        for i in 0..count {
174            table
175                .add_row(DataRow::new(vec![
176                    DataValue::Integer(i),
177                    DataValue::Integer(start + i),
178                ]))
179                .map_err(|e| anyhow!(e))?;
180        }
181
182        Ok(Arc::new(table))
183    }
184
185    fn description(&self) -> &str {
186        "Generate indexed series (0-based index with values)"
187    }
188
189    fn arg_count(&self) -> usize {
190        2 // count and optional start value
191    }
192}
193
194/// DATES - Generate date sequence
195pub struct Dates;
196
197impl TableGenerator for Dates {
198    fn name(&self) -> &str {
199        "DATES"
200    }
201
202    fn columns(&self) -> Vec<DataColumn> {
203        vec![DataColumn {
204            name: "date".to_string(),
205            data_type: DataType::String, // We'll store dates as ISO strings
206            nullable: false,
207            unique_values: Some(0),
208            null_count: 0,
209            metadata: HashMap::new(),
210            qualified_name: None,
211            source_table: None,
212        }]
213    }
214
215    fn generate(&self, args: Vec<DataValue>) -> Result<Arc<DataTable>> {
216        use chrono::{Duration, NaiveDate};
217
218        if args.len() < 2 {
219            return Err(anyhow!(
220                "DATES requires at least 2 arguments (start_date, end_date)"
221            ));
222        }
223
224        // Parse start date
225        let start_str = match &args[0] {
226            DataValue::String(s) => s,
227            _ => return Err(anyhow!("DATES start_date must be a string")),
228        };
229
230        let start = NaiveDate::parse_from_str(start_str, "%Y-%m-%d")
231            .map_err(|e| anyhow!("Invalid start date format (expected YYYY-MM-DD): {}", e))?;
232
233        // Parse end date
234        let end_str = match &args[1] {
235            DataValue::String(s) => s,
236            _ => return Err(anyhow!("DATES end_date must be a string")),
237        };
238
239        let end = NaiveDate::parse_from_str(end_str, "%Y-%m-%d")
240            .map_err(|e| anyhow!("Invalid end date format (expected YYYY-MM-DD): {}", e))?;
241
242        // Get step (default to 1 day)
243        let step_days = if args.len() > 2 {
244            match args[2] {
245                DataValue::Integer(n) => n,
246                DataValue::Float(f) => f as i64,
247                _ => 1,
248            }
249        } else {
250            1
251        };
252
253        if step_days == 0 {
254            return Err(anyhow!("DATES step cannot be zero"));
255        }
256
257        let mut table = DataTable::new("dates");
258        table.add_column(DataColumn::new("date"));
259
260        let mut current = start;
261        let step = Duration::days(step_days);
262
263        if step_days > 0 {
264            while current <= end {
265                table
266                    .add_row(DataRow::new(vec![DataValue::String(
267                        current.format("%Y-%m-%d").to_string(),
268                    )]))
269                    .map_err(|e| anyhow!(e))?;
270                current = current + step;
271            }
272        } else {
273            while current >= end {
274                table
275                    .add_row(DataRow::new(vec![DataValue::String(
276                        current.format("%Y-%m-%d").to_string(),
277                    )]))
278                    .map_err(|e| anyhow!(e))?;
279                current = current + step;
280            }
281        }
282
283        Ok(Arc::new(table))
284    }
285
286    fn description(&self) -> &str {
287        "Generate date sequence between two dates"
288    }
289
290    fn arg_count(&self) -> usize {
291        3 // start_date, end_date, optional step_days
292    }
293}