Skip to content

Commit 299e469

Browse files
ok
1 parent 9c3184e commit 299e469

File tree

8 files changed

+778
-177
lines changed

8 files changed

+778
-177
lines changed

crates/pgt_completions/src/context/grant_parser.rs

Lines changed: 430 additions & 0 deletions
Large diffs are not rendered by default.

crates/pgt_completions/src/context/mod.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use std::{
22
cmp,
33
collections::{HashMap, HashSet},
44
};
5+
mod grant_parser;
6+
mod parser_helper;
57
mod policy_parser;
68

79
use pgt_schema_cache::SchemaCache;
@@ -13,7 +15,10 @@ use pgt_treesitter_queries::{
1315

1416
use crate::{
1517
NodeText,
16-
context::policy_parser::{PolicyParser, PolicyStmtKind},
18+
context::{
19+
grant_parser::GrantParser,
20+
policy_parser::{PolicyParser, PolicyStmtKind},
21+
},
1722
sanitization::SanitizedCompletionParams,
1823
};
1924

@@ -36,6 +41,7 @@ pub enum WrappingClause<'a> {
3641
SetStatement,
3742
AlterRole,
3843
DropRole,
44+
Grant,
3945
}
4046

4147
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
@@ -192,14 +198,48 @@ impl<'a> CompletionContext<'a> {
192198
// We infer the context manually.
193199
if PolicyParser::looks_like_policy_stmt(&params.text) {
194200
ctx.gather_policy_context();
201+
} else if GrantParser::looks_like_grant_stmt(&params.text) {
202+
ctx.gather_grant_context();
195203
} else {
196204
ctx.gather_tree_context();
197205
ctx.gather_info_from_ts_queries();
198206
}
199207

208+
println!("{:#?}", ctx.text);
209+
println!("{:#?}", ctx.wrapping_clause_type);
210+
println!("{:#?}", ctx.node_under_cursor);
211+
200212
ctx
201213
}
202214

215+
fn gather_grant_context(&mut self) {
216+
let grant_context = GrantParser::get_context(self.text, self.position);
217+
218+
self.node_under_cursor = Some(NodeUnderCursor::CustomNode {
219+
text: grant_context.node_text.into(),
220+
range: grant_context.node_range,
221+
kind: grant_context.node_kind.clone(),
222+
});
223+
224+
if grant_context.node_kind == "grant_table" {
225+
self.schema_or_alias_name = grant_context.schema_name.clone();
226+
}
227+
228+
if grant_context.table_name.is_some() {
229+
let mut new = HashSet::new();
230+
new.insert(grant_context.table_name.unwrap());
231+
self.mentioned_relations
232+
.insert(grant_context.schema_name, new);
233+
}
234+
235+
self.wrapping_clause_type = match grant_context.node_kind.as_str() {
236+
"keyword_grant" => Some(WrappingClause::Grant),
237+
"grant_role" => Some(WrappingClause::ToRoleAssignment),
238+
"grant_table" => Some(WrappingClause::From),
239+
_ => None,
240+
};
241+
}
242+
203243
fn gather_policy_context(&mut self) {
204244
let policy_context = PolicyParser::get_context(self.text, self.position);
205245

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use pgt_text_size::{TextRange, TextSize};
2+
3+
#[derive(Clone, Debug, PartialEq, Eq)]
4+
pub(crate) struct WordWithIndex {
5+
word: String,
6+
start: usize,
7+
end: usize,
8+
}
9+
10+
impl WordWithIndex {
11+
pub(crate) fn is_under_cursor(&self, cursor_pos: usize) -> bool {
12+
self.start <= cursor_pos && self.end > cursor_pos
13+
}
14+
15+
pub(crate) fn get_range(&self) -> TextRange {
16+
let start: u32 = self.start.try_into().expect("Text too long");
17+
let end: u32 = self.end.try_into().expect("Text too long");
18+
TextRange::new(TextSize::from(start), TextSize::from(end))
19+
}
20+
21+
pub(crate) fn get_word_without_quotes(&self) -> String {
22+
self.word.replace('"', "")
23+
}
24+
25+
pub(crate) fn get_word(&self) -> String {
26+
self.word.clone()
27+
}
28+
}
29+
30+
/// Note: A policy name within quotation marks will be considered a single word.
31+
pub(crate) fn sql_to_words(sql: &str) -> Result<Vec<WordWithIndex>, String> {
32+
let mut words = vec![];
33+
34+
let mut start_of_word: Option<usize> = None;
35+
let mut current_word = String::new();
36+
let mut in_quotation_marks = false;
37+
38+
for (current_position, current_char) in sql.char_indices() {
39+
if (current_char.is_ascii_whitespace() || current_char == ';')
40+
&& !current_word.is_empty()
41+
&& start_of_word.is_some()
42+
&& !in_quotation_marks
43+
{
44+
words.push(WordWithIndex {
45+
word: current_word,
46+
start: start_of_word.unwrap(),
47+
end: current_position,
48+
});
49+
50+
current_word = String::new();
51+
start_of_word = None;
52+
} else if (current_char.is_ascii_whitespace() || current_char == ';')
53+
&& current_word.is_empty()
54+
{
55+
// do nothing
56+
} else if current_char == '"' && start_of_word.is_none() {
57+
in_quotation_marks = true;
58+
current_word.push(current_char);
59+
start_of_word = Some(current_position);
60+
} else if current_char == '"' && start_of_word.is_some() {
61+
current_word.push(current_char);
62+
in_quotation_marks = false;
63+
} else if start_of_word.is_some() {
64+
current_word.push(current_char)
65+
} else {
66+
start_of_word = Some(current_position);
67+
current_word.push(current_char);
68+
}
69+
}
70+
71+
if let Some(start_of_word) = start_of_word {
72+
if !current_word.is_empty() {
73+
words.push(WordWithIndex {
74+
word: current_word,
75+
start: start_of_word,
76+
end: sql.len(),
77+
});
78+
}
79+
}
80+
81+
if in_quotation_marks {
82+
Err("String was not closed properly.".into())
83+
} else {
84+
Ok(words)
85+
}
86+
}
87+
88+
#[cfg(test)]
89+
mod tests {
90+
use crate::context::parser_helper::{WordWithIndex, sql_to_words};
91+
92+
#[test]
93+
fn determines_positions_correctly() {
94+
let query = "\ncreate policy \"my cool pol\"\n\ton auth.users\n\tas permissive\n\tfor select\n\t\tto public\n\t\tusing (true);".to_string();
95+
96+
let words = sql_to_words(query.as_str()).unwrap();
97+
98+
assert_eq!(words[0], to_word("create", 1, 7));
99+
assert_eq!(words[1], to_word("policy", 8, 14));
100+
assert_eq!(words[2], to_word("\"my cool pol\"", 15, 28));
101+
assert_eq!(words[3], to_word("on", 30, 32));
102+
assert_eq!(words[4], to_word("auth.users", 33, 43));
103+
assert_eq!(words[5], to_word("as", 45, 47));
104+
assert_eq!(words[6], to_word("permissive", 48, 58));
105+
assert_eq!(words[7], to_word("for", 60, 63));
106+
assert_eq!(words[8], to_word("select", 64, 70));
107+
assert_eq!(words[9], to_word("to", 73, 75));
108+
assert_eq!(words[10], to_word("public", 78, 84));
109+
assert_eq!(words[11], to_word("using", 87, 92));
110+
assert_eq!(words[12], to_word("(true)", 93, 99));
111+
}
112+
113+
#[test]
114+
fn handles_schemas_in_quotation_marks() {
115+
let query = r#"grant select on "public"."users""#.to_string();
116+
117+
let words = sql_to_words(query.as_str()).unwrap();
118+
119+
assert_eq!(words[0], to_word("grant", 0, 5));
120+
assert_eq!(words[1], to_word("select", 6, 12));
121+
assert_eq!(words[2], to_word("on", 13, 15));
122+
assert_eq!(words[3], to_word(r#""public"."users""#, 16, 32));
123+
}
124+
125+
fn to_word(word: &str, start: usize, end: usize) -> WordWithIndex {
126+
WordWithIndex {
127+
word: word.into(),
128+
start,
129+
end,
130+
}
131+
}
132+
}

0 commit comments

Comments
 (0)