Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions crates/squawk_ide/src/binder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ fn bind_file(b: &mut Binder, file: &ast::SourceFile) {
fn bind_stmt(b: &mut Binder, stmt: ast::Stmt) {
match stmt {
ast::Stmt::CreateTable(create_table) => bind_create_table(b, create_table),
ast::Stmt::CreateForeignTable(create_foreign_table) => {
bind_create_foreign_table(b, create_foreign_table)
}
ast::Stmt::CreateIndex(create_index) => bind_create_index(b, create_index),
ast::Stmt::CreateFunction(create_function) => bind_create_function(b, create_function),
ast::Stmt::CreateAggregate(create_aggregate) => bind_create_aggregate(b, create_aggregate),
Expand Down Expand Up @@ -122,6 +125,29 @@ fn bind_create_table(b: &mut Binder, create_table: ast::CreateTable) {
b.scopes[root].insert(table_name, table_id);
}

fn bind_create_foreign_table(b: &mut Binder, create_foreign_table: ast::CreateForeignTable) {
let Some(path) = create_foreign_table.path() else {
return;
};
let Some(table_name) = item_name(&path) else {
return;
};
let name_ptr = path_to_ptr(&path);
let Some(schema) = schema_name(b, &path, false) else {
return;
};

let table_id = b.symbols.alloc(Symbol {
kind: SymbolKind::Table,
ptr: name_ptr,
schema,
params: None,
});

let root = b.root_scope();
b.scopes[root].insert(table_name, table_id);
}

fn bind_create_index(b: &mut Binder, create_index: ast::CreateIndex) {
let Some(name) = create_index.name() else {
return;
Expand Down
14 changes: 14 additions & 0 deletions crates/squawk_ide/src/classify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,12 @@ pub(crate) enum NameClass {
create_table: ast::CreateTable,
column: ast::Column,
},
ColumnDefinitionForeignTable {
create_foreign_table: ast::CreateForeignTable,
column: ast::Column,
},
CreateTable(ast::CreateTable),
CreateForeignTable(ast::CreateForeignTable),
WithTable(ast::WithTable),
CreateIndex(ast::CreateIndex),
CreateSequence(ast::CreateSequence),
Expand Down Expand Up @@ -461,6 +466,15 @@ pub(crate) fn classify_name(name: &ast::Name) -> Option<NameClass> {
}
return Some(NameClass::CreateTable(create_table));
}
if let Some(create_foreign_table) = ast::CreateForeignTable::cast(ancestor.clone()) {
if let Some(column) = column_parent {
return Some(NameClass::ColumnDefinitionForeignTable {
create_foreign_table,
column,
});
}
return Some(NameClass::CreateForeignTable(create_foreign_table));
}
if let Some(create_index) = ast::CreateIndex::cast(ancestor.clone()) {
return Some(NameClass::CreateIndex(create_index));
}
Expand Down
34 changes: 34 additions & 0 deletions crates/squawk_ide/src/goto_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,40 @@ create table t$0(x bigint, y bigint);
");
}

#[test]
fn goto_foreign_table_column() {
assert_snapshot!(goto("
create foreign table ft(a int)
server s;

select a$0 from ft;
"), @r"
╭▸
2 │ create foreign table ft(a int)
│ ─ 2. destination
5 │ select a from ft;
╰╴ ─ 1. source
");
}

#[test]
fn goto_foreign_table_definition() {
assert_snapshot!(goto("
create foreign table ft(a int)
server s;

select a from ft$0;
"), @r"
╭▸
2 │ create foreign table ft(a int)
│ ── 2. destination
5 │ select a from ft;
╰╴ ─ 1. source
");
}

#[test]
fn goto_foreign_key_references_table() {
assert_snapshot!(goto("
Expand Down
36 changes: 33 additions & 3 deletions crates/squawk_ide/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,18 @@ pub fn hover(file: &ast::SourceFile, offset: TextSize) -> Option<String> {
create_table,
column,
} => return hover_column_definition(&create_table, &column, &binder),
NameClass::ColumnDefinitionForeignTable {
create_foreign_table,
column,
} => {
return hover_column_definition(&create_foreign_table, &column, &binder);
}
NameClass::CreateTable(create_table) => {
return format_create_table(&create_table, &binder);
}
NameClass::CreateForeignTable(create_foreign_table) => {
return format_create_foreign_table(&create_foreign_table, &binder);
}
NameClass::WithTable(with_table) => return format_with_table(&with_table),
NameClass::CreateIndex(create_index) => {
return format_create_index(&create_index, &binder);
Expand Down Expand Up @@ -268,7 +277,7 @@ fn format_hover_for_column_node(
let create_table = column
.syntax()
.ancestors()
.find_map(ast::CreateTable::cast)?;
.find_map(ast::CreateTableLike::cast)?;
let path = create_table.path()?;
let (schema, table_name) = resolve::resolve_table_info(binder, &path)?;

Expand Down Expand Up @@ -314,7 +323,7 @@ fn hover_composite_type_field(
}

fn hover_column_definition(
create_table: &ast::CreateTable,
create_table: &impl ast::HasCreateTable,
column: &ast::Column,
binder: &binder::Binder,
) -> Option<String> {
Expand Down Expand Up @@ -360,6 +369,9 @@ fn hover_table_from_ptr(
resolve::TableSource::CreateTable(create_table) => {
format_create_table(&create_table, binder)
}
resolve::TableSource::CreateForeignTable(create_foreign_table) => {
format_create_foreign_table(&create_foreign_table, binder)
}
}
}

Expand Down Expand Up @@ -448,14 +460,17 @@ fn hover_qualified_star_columns(
resolve::TableSource::CreateTable(create_table) => {
hover_qualified_star_columns_from_table(&create_table, binder)
}
resolve::TableSource::CreateForeignTable(create_foreign_table) => {
hover_qualified_star_columns_from_table(&create_foreign_table, binder)
}
resolve::TableSource::CreateView(create_view) => {
hover_qualified_star_columns_from_view(&create_view, binder)
}
}
}

fn hover_qualified_star_columns_from_table(
create_table: &ast::CreateTable,
create_table: &impl ast::HasCreateTable,
binder: &binder::Binder,
) -> Option<String> {
let path = create_table.path()?;
Expand Down Expand Up @@ -641,6 +656,21 @@ fn format_create_table(create_table: &ast::CreateTable, binder: &binder::Binder)
Some(format!("table {}.{}{}", schema, table_name, args))
}

fn format_create_foreign_table(
create_foreign_table: &ast::CreateForeignTable,
binder: &binder::Binder,
) -> Option<String> {
let path = create_foreign_table.path()?;
let (schema, table_name) = resolve::resolve_table_info(binder, &path)?;
let schema = schema.to_string();
let args = create_foreign_table
.table_arg_list()
.map(|list| list.syntax().text().to_string())
.unwrap_or_default();

Some(format!("foreign table {}.{}{}", schema, table_name, args))
}

fn format_create_view(create_view: &ast::CreateView, binder: &binder::Binder) -> Option<String> {
let path = create_view.path()?;
let (schema, view_name) = resolve::resolve_view_info(binder, &path)?;
Expand Down
40 changes: 26 additions & 14 deletions crates/squawk_ide/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,7 @@ fn resolve_column_for_path(

let create_table = table_name_node
.ancestors()
.find_map(ast::CreateTable::cast)?;
.find_map(ast::CreateTableLike::cast)?;

find_column_in_create_table(&create_table, &column_name)
}
Expand Down Expand Up @@ -882,8 +882,10 @@ fn resolve_select_qualified_column(

if let Some(table_ptr) = resolve_table(binder, &table_name, &schema, position) {
let table_name_node = table_ptr.to_node(root);

if let Some(create_table) = table_name_node.ancestors().find_map(ast::CreateTable::cast) {
if let Some(create_table) = table_name_node
.ancestors()
.find_map(ast::CreateTableLike::cast)
{
// 1. Try to find a matching column (columns take precedence)
if let Some(ptr) = find_column_in_create_table(&create_table, &column_name) {
return Some(ptr);
Expand Down Expand Up @@ -950,7 +952,10 @@ fn resolve_column_from_table_or_view(
if let Some(table_ptr) = resolve_table(binder, table_name, schema, position) {
let table_name_node = table_ptr.to_node(root);

if let Some(create_table) = table_name_node.ancestors().find_map(ast::CreateTable::cast) {
if let Some(create_table) = table_name_node
.ancestors()
.find_map(ast::CreateTableLike::cast)
{
// 1. try to find a matching column
if let Some(ptr) = find_column_in_create_table(&create_table, column_name) {
return Some(ptr);
Expand Down Expand Up @@ -1204,7 +1209,7 @@ fn resolve_from_item_for_fn_call_column(
let table_name_node = table_ptr.to_node(root);
let create_table = table_name_node
.ancestors()
.find_map(ast::CreateTable::cast)?;
.find_map(ast::CreateTableLike::cast)?;

find_column_in_create_table(&create_table, column_name)
}
Expand Down Expand Up @@ -1324,19 +1329,18 @@ pub(crate) fn extract_column_name(col: &ast::Column) -> Option<Name> {
}

pub(crate) fn find_column_in_create_table(
create_table: &ast::CreateTable,
create_table: &impl ast::HasCreateTable,
column_name: &Name,
) -> Option<SyntaxNodePtr> {
create_table.table_arg_list()?.args().find_map(|arg| {
if let ast::TableArg::Column(column) = arg
for arg in create_table.table_arg_list()?.args() {
if let ast::TableArg::Column(column) = &arg
&& let Some(name) = column.name()
&& Name::from_node(&name) == *column_name
{
return Some(SyntaxNodePtr::new(name.syntax()));
} else {
None
}
})
}
None
}

// TODO: this is similar to the CTE funcs, maybe we can simplify
Expand Down Expand Up @@ -1748,6 +1752,7 @@ pub(crate) enum TableSource {
WithTable(ast::WithTable),
CreateView(ast::CreateView),
CreateTable(ast::CreateTable),
CreateForeignTable(ast::CreateForeignTable),
}

pub(crate) fn find_table_source(node: &SyntaxNode) -> Option<TableSource> {
Expand All @@ -1760,9 +1765,13 @@ pub(crate) fn find_table_source(node: &SyntaxNode) -> Option<TableSource> {
return Some(TableSource::CreateView(create_view));
}

if let Some(create_table) = ast::CreateTable::cast(ancestor) {
if let Some(create_table) = ast::CreateTable::cast(ancestor.clone()) {
return Some(TableSource::CreateTable(create_table));
}

if let Some(create_foreign_table) = ast::CreateForeignTable::cast(ancestor) {
return Some(TableSource::CreateForeignTable(create_foreign_table));
}
}

None
Expand Down Expand Up @@ -1880,7 +1889,10 @@ fn count_columns_for_from_item(
if let Some(table_ptr) = resolve_table(binder, &table_name, &schema, position) {
let table_name_node = table_ptr.to_node(root);

if let Some(create_table) = table_name_node.ancestors().find_map(ast::CreateTable::cast) {
if let Some(create_table) = table_name_node
.ancestors()
.find_map(ast::CreateTableLike::cast)
{
let mut count: usize = 0;
if let Some(args) = create_table.table_arg_list() {
for arg in args.args() {
Expand Down Expand Up @@ -2104,7 +2116,7 @@ pub(crate) fn resolve_sequence_info(binder: &Binder, path: &ast::Path) -> Option
resolve_symbol_info(binder, path, SymbolKind::Sequence)
}

pub(crate) fn collect_table_columns(create_table: &ast::CreateTable) -> Vec<ast::Column> {
pub(crate) fn collect_table_columns(create_table: &impl ast::HasCreateTable) -> Vec<ast::Column> {
let mut columns = vec![];
if let Some(arg_list) = create_table.table_arg_list() {
for arg in arg_list.args() {
Expand Down
14 changes: 1 addition & 13 deletions crates/squawk_syntax/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ use crate::syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken};
use squawk_parser::SyntaxKind;

pub use self::{
// generated::{nodes::*, tokens::*},
generated::tokens::*,
nodes::*,
// node_ext::{
Expand All @@ -46,18 +45,7 @@ pub use self::{
// },
// operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
// token_ext::{CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix},
traits::{
// AttrDocCommentIter, DocCommentIter,
HasArgList, // HasAttrs, HasDocComments, HasGenericArgs,
HasIfExists,
HasIfNotExists, // HasTypeBounds,
// HasVisibility,
// HasGenericParams, HasLoopBody,
HasName,
HasParamList,
HasWithClause,
NameLike,
},
traits::{HasCreateTable, HasParamList, HasWithClause, NameLike},
};

/// The main trait to go from untyped `SyntaxNode` to a typed ast. The
Expand Down
4 changes: 4 additions & 0 deletions crates/squawk_syntax/src/ast/node_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,10 @@ impl ast::HasWithClause for ast::Insert {}
impl ast::HasWithClause for ast::Update {}
impl ast::HasWithClause for ast::Delete {}

impl ast::HasCreateTable for ast::CreateTable {}
impl ast::HasCreateTable for ast::CreateForeignTable {}
impl ast::HasCreateTable for ast::CreateTableLike {}

#[test]
fn index_expr() {
let source_code = "
Expand Down
44 changes: 44 additions & 0 deletions crates/squawk_syntax/src/ast/nodes.rs
Original file line number Diff line number Diff line change
@@ -1 +1,45 @@
pub use crate::ast::generated::nodes::*;
use crate::{
SyntaxNode,
ast::{self, AstNode, support},
};

// TODO: Initial attempt to try and unify the CreateTable and
// CreateForeignTable. Not sure this is the right approach, we may want to be
// more general, like TableSource, which can be a View, CTE, Table,
// ForeignTable, Subquery, etc.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CreateTableLike {
pub(crate) syntax: SyntaxNode,
}
impl CreateTableLike {
#[inline]
pub fn path(&self) -> Option<ast::Path> {
support::child(&self.syntax)
}
#[inline]
pub fn table_arg_list(&self) -> Option<ast::TableArgList> {
support::child(&self.syntax)
}
}
impl AstNode for CreateTableLike {
#[inline]
fn can_cast(kind: ast::SyntaxKind) -> bool {
matches!(
kind,
ast::SyntaxKind::CREATE_TABLE | ast::SyntaxKind::CREATE_FOREIGN_TABLE
)
}
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
#[inline]
fn syntax(&self) -> &SyntaxNode {
&self.syntax
}
}
Loading
Loading