Skip to content

Commit caee75a

Browse files
Merge branch 'main' into feat/improved-type-msgs
2 parents 40e4c93 + 43df7de commit caee75a

File tree

11 files changed

+181
-8
lines changed

11 files changed

+181
-8
lines changed

.sqlx/query-b0163e58e9c646e3af524174081b74ab7e7938e9516ea21513265c49d304b6ea.json renamed to .sqlx/query-1cc58ddce2b52b5d6f6519cde339f258bce50342c2d5cce036dba4f9062cf811.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/pgt_hover/src/hovered_node.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ impl HoveredNode {
2424
pub(crate) fn get(ctx: &pgt_treesitter::context::TreesitterContext) -> Option<Self> {
2525
let node_content = ctx.get_node_under_cursor_content()?;
2626

27+
if looks_like_sql_param(node_content.as_str()) {
28+
return None;
29+
}
30+
2731
let under_cursor = ctx.node_under_cursor.as_ref()?;
2832

2933
match under_cursor.kind() {
@@ -114,3 +118,10 @@ impl HoveredNode {
114118
}
115119
}
116120
}
121+
122+
fn looks_like_sql_param(content: &str) -> bool {
123+
(content.starts_with("$") && !content.starts_with("$$"))
124+
|| (content.starts_with(":") && !content.starts_with("::"))
125+
|| (content.starts_with("@"))
126+
|| content.starts_with("?")
127+
}

crates/pgt_hover/tests/hover_integration_tests.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,3 +518,44 @@ async fn test_grant_table_hover(test_db: PgPool) {
518518

519519
test_hover_at_cursor("grant_select", query, None, &test_db).await;
520520
}
521+
522+
#[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")]
523+
async fn no_hover_results_over_params(test_db: PgPool) {
524+
let setup = r#"
525+
create table users (
526+
id serial primary key,
527+
name text
528+
);
529+
"#;
530+
531+
test_db.execute(setup).await.unwrap();
532+
533+
{
534+
let query = format!(
535+
"select * from users where name = $n{}ame;",
536+
QueryWithCursorPosition::cursor_marker()
537+
);
538+
test_hover_at_cursor("dollar-param", query, None, &test_db).await;
539+
}
540+
{
541+
let query = format!(
542+
"select * from users where name = :n{}ame;",
543+
QueryWithCursorPosition::cursor_marker()
544+
);
545+
test_hover_at_cursor("colon-param", query, None, &test_db).await;
546+
}
547+
{
548+
let query = format!(
549+
"select * from users where name = @n{}ame;",
550+
QueryWithCursorPosition::cursor_marker()
551+
);
552+
test_hover_at_cursor("at-param", query, None, &test_db).await;
553+
}
554+
{
555+
let query = format!(
556+
"select * from users where name = ?n{}ame;",
557+
QueryWithCursorPosition::cursor_marker()
558+
);
559+
test_hover_at_cursor("questionmark-param", query, None, &test_db).await;
560+
}
561+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
source: crates/pgt_hover/tests/hover_integration_tests.rs
3+
expression: snapshot
4+
---
5+
# Input
6+
```sql
7+
select * from users where name = @name;
8+
↑ hovered here
9+
```
10+
11+
# Hover Results
12+
No hover information found.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
source: crates/pgt_hover/tests/hover_integration_tests.rs
3+
expression: snapshot
4+
---
5+
# Input
6+
```sql
7+
select * from users where name = :name;
8+
↑ hovered here
9+
```
10+
11+
# Hover Results
12+
No hover information found.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
source: crates/pgt_hover/tests/hover_integration_tests.rs
3+
expression: snapshot
4+
---
5+
# Input
6+
```sql
7+
select * from users where name = $name;
8+
↑ hovered here
9+
```
10+
11+
# Hover Results
12+
No hover information found.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
source: crates/pgt_hover/tests/hover_integration_tests.rs
3+
expression: snapshot
4+
---
5+
# Input
6+
```sql
7+
select * from users where name = ?name;
8+
↑ hovered here
9+
```
10+
11+
# Hover Results
12+
No hover information found.

crates/pgt_schema_cache/src/queries/columns.sql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ with
1919
available_indexes as (
2020
select
2121
unnest (ix.indkey) as attnum,
22-
ix.indisprimary as is_primary,
23-
ix.indisunique as is_unique,
22+
bool_or(ix.indisprimary) as is_primary,
23+
bool_or(ix.indisunique) as is_unique,
2424
ix.indrelid as table_oid
2525
from
2626
pg_catalog.pg_class c
2727
join pg_catalog.pg_index ix on c.oid = ix.indexrelid
2828
where
2929
c.relkind = 'i'
30+
group by table_oid, attnum
3031
)
3132
select
3233
atts.attname as name,

crates/pgt_schema_cache/src/schema_cache.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,9 @@ pub trait SchemaCacheItem {
165165

166166
#[cfg(test)]
167167
mod tests {
168-
use sqlx::PgPool;
168+
use std::collections::HashSet;
169+
170+
use sqlx::{Executor, PgPool};
169171

170172
use crate::SchemaCache;
171173

@@ -175,4 +177,33 @@ mod tests {
175177
.await
176178
.expect("Couldnt' load Schema Cache");
177179
}
180+
181+
#[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")]
182+
async fn it_does_not_have_duplicate_entries(test_db: PgPool) {
183+
// we had some duplicate columns in the schema_cache because of indices including the same column multiple times.
184+
// the columns were unnested as duplicates in the query
185+
let setup = r#"
186+
CREATE TABLE public.mfa_factors (
187+
id uuid PRIMARY KEY,
188+
factor_name text NOT NULL
189+
);
190+
191+
-- a second index on id!
192+
CREATE INDEX idx_mfa_user_factor ON public.mfa_factors(id, factor_name);
193+
"#;
194+
195+
test_db.execute(setup).await.unwrap();
196+
197+
let cache = SchemaCache::load(&test_db)
198+
.await
199+
.expect("Couldn't load Schema Cache");
200+
201+
let set: HashSet<String> = cache
202+
.columns
203+
.iter()
204+
.map(|c| format!("{}.{}.{}", c.schema_name, c.table_name, c.name))
205+
.collect();
206+
207+
assert_eq!(set.len(), cache.columns.len());
208+
}
178209
}

crates/pgt_treesitter/src/context/mod.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,9 @@ impl<'a> TreesitterContext<'a> {
470470
}
471471

472472
// We have arrived at the leaf node
473-
if current_node.child_count() == 0 {
473+
if current_node.child_count() == 0
474+
|| current_node.first_child_for_byte(self.position).is_none()
475+
{
474476
self.node_under_cursor = Some(NodeUnderCursor::from(current_node));
475477
return;
476478
}
@@ -1214,4 +1216,43 @@ mod tests {
12141216
_ => unreachable!(),
12151217
}
12161218
}
1219+
1220+
#[test]
1221+
fn does_not_overflow_callstack_on_smaller_treesitter_child() {
1222+
let query = format!(
1223+
r#"select * from persons where id = @i{}d;"#,
1224+
QueryWithCursorPosition::cursor_marker()
1225+
);
1226+
1227+
/*
1228+
The query (currently) yields the following treesitter tree for the WHERE clause:
1229+
1230+
where [29..43] 'where id = @id'
1231+
keyword_where [29..34] 'where'
1232+
binary_expression [35..43] 'id = @id'
1233+
field [35..37] 'id'
1234+
identifier [35..37] 'id'
1235+
= [38..39] '='
1236+
field [40..43] '@id'
1237+
identifier [40..43] '@id'
1238+
@ [40..41] '@'
1239+
1240+
You can see that the '@' is a child of the "identifier" but has a range smaller than its parent's.
1241+
This would crash our context parsing because, at position 42, we weren't at the leaf node but also couldn't
1242+
go to a child on that position.
1243+
*/
1244+
1245+
let (position, text) = QueryWithCursorPosition::from(query).get_text_and_position();
1246+
1247+
let tree = get_tree(text.as_str());
1248+
1249+
let params = TreeSitterContextParams {
1250+
position: (position as u32).into(),
1251+
text: &text,
1252+
tree: &tree,
1253+
};
1254+
1255+
// should simply not panic
1256+
let _ = TreesitterContext::new(params);
1257+
}
12171258
}

0 commit comments

Comments
 (0)