1use crate::dialect::Dialect;
2use crate::parser::SqlParser;
3use crate::schema::Schema;
4use async_trait::async_trait;
5use tower_lsp::lsp_types::{
6 CompletionItem, CompletionItemKind, Diagnostic, Hover, Location, MarkedString, Position,
7};
8
9pub struct HiveDialect {
10 parser: std::sync::Mutex<SqlParser>,
11}
12
13impl Default for HiveDialect {
14 fn default() -> Self {
15 Self::new()
16 }
17}
18
19impl HiveDialect {
20 pub fn new() -> Self {
21 Self {
22 parser: std::sync::Mutex::new(SqlParser::new()),
23 }
24 }
25
26 fn create_keyword_item(&self, keyword: &str) -> CompletionItem {
27 CompletionItem {
28 label: keyword.to_string(),
29 kind: Some(CompletionItemKind::KEYWORD),
30 detail: Some(format!("Hive keyword: {}", keyword)),
31 documentation: None,
32 deprecated: None,
33 preselect: None,
34 sort_text: Some(format!("0{}", keyword)),
35 filter_text: None,
36 insert_text: Some(keyword.to_string()),
37 insert_text_format: None,
38 insert_text_mode: None,
39 text_edit: None,
40 additional_text_edits: None,
41 commit_characters: None,
42 command: None,
43 data: None,
44 tags: None,
45 label_details: None,
46 }
47 }
48
49 fn create_table_item(&self, table: &crate::schema::Table) -> CompletionItem {
50 CompletionItem {
51 label: table.name.clone(),
52 kind: Some(CompletionItemKind::CLASS),
53 detail: Some(format!("Table: {}", table.name)),
54 documentation: table
55 .comment
56 .clone()
57 .map(tower_lsp::lsp_types::Documentation::String),
58 deprecated: None,
59 preselect: None,
60 sort_text: Some(format!("1{}", table.name)),
61 filter_text: None,
62 insert_text: Some(table.name.clone()),
63 insert_text_format: None,
64 insert_text_mode: None,
65 text_edit: None,
66 additional_text_edits: None,
67 commit_characters: None,
68 command: None,
69 data: None,
70 tags: None,
71 label_details: None,
72 }
73 }
74
75 fn create_column_item(
76 &self,
77 column: &crate::schema::Column,
78 table_name: Option<&str>,
79 ) -> CompletionItem {
80 let label = if let Some(table) = table_name {
81 format!("{}.{}", table, column.name)
82 } else {
83 column.name.clone()
84 };
85
86 CompletionItem {
87 label,
88 kind: Some(CompletionItemKind::FIELD),
89 detail: Some(format!("Column: {} ({})", column.name, column.data_type)),
90 documentation: column
91 .comment
92 .clone()
93 .map(tower_lsp::lsp_types::Documentation::String),
94 deprecated: None,
95 preselect: None,
96 sort_text: Some(format!("2{}", column.name)),
97 filter_text: None,
98 insert_text: Some(column.name.clone()),
99 insert_text_format: None,
100 insert_text_mode: None,
101 text_edit: None,
102 additional_text_edits: None,
103 commit_characters: None,
104 command: None,
105 data: None,
106 tags: None,
107 label_details: None,
108 }
109 }
110}
111
112#[async_trait]
113impl Dialect for HiveDialect {
114 fn name(&self) -> &str {
115 "hive"
116 }
117
118 async fn parse(&self, sql: &str, _schema: Option<&Schema>) -> Vec<Diagnostic> {
119 let mut parser = self.parser.lock().unwrap();
120 let parse_result = parser.parse(sql);
121 parse_result.diagnostics
122 }
123
124 async fn completion(
125 &self,
126 sql: &str,
127 position: Position,
128 schema: Option<&Schema>,
129 ) -> Vec<CompletionItem> {
130 let mut parser = self.parser.lock().unwrap();
131 let parse_result = parser.parse(sql);
132
133 let context = if let Some(tree) = &parse_result.tree {
134 if let Some(node) = parser.get_node_at_position(tree, position) {
135 parser.analyze_completion_context(node, sql, position)
136 } else {
137 crate::parser::CompletionContext::Default
138 }
139 } else {
140 crate::parser::CompletionContext::Default
141 };
142
143 let mut items = Vec::new();
144 let keywords = &[
145 "SELECT",
146 "FROM",
147 "WHERE",
148 "INSERT",
149 "OVERWRITE",
150 "INTO",
151 "TABLE",
152 "PARTITION",
153 "CREATE",
154 "DROP",
155 "ALTER",
156 "LOAD",
157 "DATA",
158 "LOCAL",
159 "INPATH",
160 "EXTERNAL",
161 "STORED",
162 "AS",
163 "TEXTFILE",
164 "PARQUET",
165 "ORC",
166 "SEQUENCEFILE",
167 "RCFILE",
168 "JOIN",
169 "INNER",
170 "LEFT",
171 "RIGHT",
172 "FULL",
173 "OUTER",
174 "ON",
175 "GROUP",
176 "BY",
177 "ORDER",
178 "SORT",
179 "CLUSTER",
180 "DISTRIBUTE",
181 "HAVING",
182 "LIMIT",
183 "UNION",
184 "ALL",
185 "DISTINCT",
186 "AND",
187 "OR",
188 "NOT",
189 "IN",
190 "LIKE",
191 "RLIKE",
192 "BETWEEN",
193 "IS",
194 "NULL",
195 "CASE",
196 "WHEN",
197 "THEN",
198 "ELSE",
199 "END",
200 "CAST",
201 "ARRAY",
202 "MAP",
203 "STRUCT",
204 ];
205
206 match context {
207 crate::parser::CompletionContext::FromClause
208 | crate::parser::CompletionContext::JoinClause => {
209 let join_keywords: Vec<&str> = keywords
210 .iter()
211 .filter(|&&k| matches!(k, "JOIN" | "INNER" | "LEFT" | "RIGHT" | "OUTER" | "ON"))
212 .copied()
213 .collect();
214 for keyword in join_keywords {
215 items.push(self.create_keyword_item(keyword));
216 }
217 if let Some(schema) = schema {
218 for table in &schema.tables {
219 items.push(self.create_table_item(table));
220 }
221 }
222 }
223 crate::parser::CompletionContext::SelectClause => {
224 let select_keywords: Vec<&str> = keywords
225 .iter()
226 .filter(|&&k| matches!(k, "SELECT" | "DISTINCT" | "AS" | "FROM"))
227 .copied()
228 .collect();
229 for keyword in select_keywords {
230 items.push(self.create_keyword_item(keyword));
231 }
232 if let Some(schema) = schema {
233 for table in &schema.tables {
234 for column in &table.columns {
235 items.push(self.create_column_item(
236 column,
237 Some(&format!("{}.{}", schema.database, table.name)),
238 ));
239 }
240 }
241 }
242 }
243 crate::parser::CompletionContext::WhereClause => {
244 let where_keywords: Vec<&str> = keywords
245 .iter()
246 .filter(|&&k| {
247 matches!(
248 k,
249 "AND"
250 | "OR"
251 | "NOT"
252 | "IN"
253 | "LIKE"
254 | "RLIKE"
255 | "BETWEEN"
256 | "IS"
257 | "NULL"
258 )
259 })
260 .copied()
261 .collect();
262 for keyword in where_keywords {
263 items.push(self.create_keyword_item(keyword));
264 }
265 let operators = vec!["=", "<>", "!=", ">", "<", ">=", "<="];
266 for op in operators {
267 items.push(CompletionItem {
268 label: op.to_string(),
269 kind: Some(CompletionItemKind::OPERATOR),
270 detail: Some(format!("Operator: {}", op)),
271 documentation: None,
272 deprecated: None,
273 preselect: None,
274 sort_text: Some(format!("1{}", op)),
275 filter_text: None,
276 insert_text: Some(op.to_string()),
277 insert_text_format: None,
278 insert_text_mode: None,
279 text_edit: None,
280 additional_text_edits: None,
281 commit_characters: None,
282 command: None,
283 data: None,
284 tags: None,
285 label_details: None,
286 });
287 }
288 if let Some(schema) = schema {
289 for table in &schema.tables {
290 for column in &table.columns {
291 items.push(self.create_column_item(
292 column,
293 Some(&format!("{}.{}", schema.database, table.name)),
294 ));
295 }
296 }
297 }
298 }
299 crate::parser::CompletionContext::OrderByClause
300 | crate::parser::CompletionContext::GroupByClause => {
301 let keywords_list: Vec<&str> = keywords
302 .iter()
303 .filter(|&&k| {
304 matches!(k, "ASC" | "DESC" | "BY" | "SORT" | "CLUSTER" | "DISTRIBUTE")
305 })
306 .copied()
307 .collect();
308 for keyword in keywords_list {
309 items.push(self.create_keyword_item(keyword));
310 }
311 if let Some(schema) = schema {
312 for table in &schema.tables {
313 for column in &table.columns {
314 items.push(self.create_column_item(
315 column,
316 Some(&format!("{}.{}", schema.database, table.name)),
317 ));
318 }
319 }
320 }
321 }
322 crate::parser::CompletionContext::HavingClause => {
323 let having_keywords: Vec<&str> = keywords
324 .iter()
325 .filter(|&&k| {
326 matches!(
327 k,
328 "AND"
329 | "OR"
330 | "NOT"
331 | "IN"
332 | "LIKE"
333 | "RLIKE"
334 | "BETWEEN"
335 | "IS"
336 | "NULL"
337 )
338 })
339 .copied()
340 .collect();
341 for keyword in having_keywords {
342 items.push(self.create_keyword_item(keyword));
343 }
344 let aggregate_functions = vec!["COUNT", "SUM", "AVG", "MIN", "MAX"];
345 for func in aggregate_functions {
346 items.push(self.create_keyword_item(func));
347 }
348 if let Some(schema) = schema {
349 for table in &schema.tables {
350 for column in &table.columns {
351 items.push(self.create_column_item(
352 column,
353 Some(&format!("{}.{}", schema.database, table.name)),
354 ));
355 }
356 }
357 }
358 }
359 crate::parser::CompletionContext::TableColumn => {
360 if let Some(tree) = &parse_result.tree {
361 if let Some(node) = parser.get_node_at_position(tree, position) {
362 if let Some(table_name) = parser.get_table_name_for_column(node, sql) {
363 if let Some(schema) = schema {
364 if let Some(table) = schema.tables.iter().find(|t| {
365 t.name == table_name
366 || format!("{}.{}", schema.database, t.name) == table_name
367 }) {
368 for column in &table.columns {
369 items.push(self.create_column_item(column, None));
370 }
371 }
372 }
373 }
374 }
375 }
376 }
377 crate::parser::CompletionContext::Default => {
378 for keyword in keywords {
379 items.push(self.create_keyword_item(keyword));
380 }
381 if let Some(schema) = schema {
382 for table in &schema.tables {
383 items.push(self.create_table_item(table));
384 }
385 }
386 }
387 }
388
389 items
390 }
391
392 async fn hover(
393 &self,
394 sql: &str,
395 _position: Position,
396 schema: Option<&Schema>,
397 ) -> Option<Hover> {
398 if let Some(schema) = schema {
399 for table in &schema.tables {
400 if sql.contains(&table.name) {
401 return Some(Hover {
402 contents: tower_lsp::lsp_types::HoverContents::Scalar(
403 MarkedString::String(format!(
404 "Hive Table: {}.{}\n{}",
405 schema.database,
406 table.name,
407 table.comment.as_deref().unwrap_or("No description")
408 )),
409 ),
410 range: None,
411 });
412 }
413 }
414 }
415 None
416 }
417
418 async fn goto_definition(
419 &self,
420 _sql: &str,
421 _position: Position,
422 _schema: Option<&Schema>,
423 ) -> Option<Location> {
424 None
425 }
426
427 async fn references(
428 &self,
429 _sql: &str,
430 _position: Position,
431 _schema: Option<&Schema>,
432 ) -> Vec<Location> {
433 Vec::new()
434 }
435
436 async fn format(&self, sql: &str) -> String {
437 sql.split_whitespace().collect::<Vec<_>>().join(" ")
438 }
439
440 async fn validate(&self, sql: &str, schema: Option<&Schema>) -> Vec<Diagnostic> {
441 self.parse(sql, schema).await
442 }
443}