supabase-community / postgres_lsp

A Language Server for Postgres
https://supabase.com
MIT License
3.24k stars 61 forks source link

feat: add support for create procedure #85

Closed cvng closed 9 months ago

cvng commented 9 months ago

What kind of change does this PR introduce?

add support for create procedure

What is the current behavior?

panics

What is the new behavior?

parser returns test_create_procedure passes but parser produces incorrect tree

View log ```rust CreateFunctionStmt { is_procedure: true, replace: false, funcname: [ Node { node: Some( String( String { sval: "insert_data", }, ), ), }, ], parameters: [ Node { node: Some( FunctionParameter( FunctionParameter { name: "a", arg_type: Some( TypeName { names: [ Node { node: Some( String( String { sval: "pg_catalog", }, ), ), }, Node { node: Some( String( String { sval: "int4", }, ), ), }, ], type_oid: 0, setof: false, pct_type: false, typmods: [], typemod: -1, array_bounds: [], location: 31, }, ), mode: FuncParamDefault, defexpr: None, }, ), ), }, ], return_type: None, options: [ Node { node: Some( DefElem( DefElem { defnamespace: "", defname: "language", arg: Some( Node { node: Some( String( String { sval: "sql", }, ), ), }, ), defaction: DefelemUnspec, location: 56, }, ), ), }, Node { node: Some( DefElem( DefElem { defnamespace: "", defname: "as", arg: Some( Node { node: Some( List( List { items: [ Node { node: Some( String( String { sval: "insert into tbl values (a);", }, ), ), }, ], }, ), ), }, ), defaction: DefelemUnspec, location: 85, }, ), ), }, ], sql_body: None, } ```

Additional context

It looks like create procedure is parsed as an insert in valid_statements, when sql_body is not $$ dollar-quoted:

CREATE PROCEDURE insert_data(a integer, b integer) LANGUAGE SQL BEGIN ATOMIC INSERT INTO tbl VALUES (a); INSERT INTO tbl VALUES (b); END;
View log ```rust [2023-12-17T15:40:44Z DEBUG statement_parser_test] Parsing statement 0052 CREATE PROCEDURE insert_data(a integer, b integer) LANGUAGE SQL BEGIN ATOMIC INSERT INTO tbl VALUES (a); INSERT INTO tbl VALUES (b); END; [2023-12-17T15:40:44Z DEBUG parser::parser] start_node: SourceFile [2023-12-17T15:40:44Z DEBUG parser::parse::libpg_query_node] Parsing node InsertStmt( InsertStmt { relation: Some( RangeVar { catalogname: "", schemaname: "", relname: "tbl", inh: true, relpersistence: "p", alias: None, location: 12, }, ), cols: [], select_stmt: Some( Node { node: Some( SelectStmt( SelectStmt { distinct_clause: [], into_clause: None, target_list: [], from_clause: [], where_clause: None, group_clause: [], group_distinct: false, having_clause: None, window_clause: [], values_lists: [ Node { node: Some( List( List { items: [ Node { node: Some( ColumnRef( ColumnRef { fields: [ Node { node: Some( String( String { sval: "a", }, ), ), }, ], location: 24, }, ), ), }, ], }, ), ), }, ], sort_clause: [], limit_offset: None, limit_count: None, limit_option: Default, locking_clause: [], with_clause: None, op: SetopNone, all: false, larg: None, rarg: None, }, ), ), }, ), on_conflict_clause: None, returning_list: [], with_clause: None, r#override: OverridingNotSet, }, ) Parsing node InsertStmt( InsertStmt { relation: Some( RangeVar { catalogname: "", schemaname: "", relname: "tbl", inh: true, relpersistence: "p", alias: None, location: 12, }, ), cols: [], select_stmt: Some( Node { node: Some( SelectStmt( SelectStmt { distinct_clause: [], into_clause: None, target_list: [], from_clause: [], where_clause: None, group_clause: [], group_distinct: false, having_clause: None, window_clause: [], values_lists: [ Node { node: Some( List( List { items: [ Node { node: Some( ColumnRef( ColumnRef { fields: [ Node { node: Some( String( String { sval: "a", }, ), ), }, ], location: 24, }, ), ), }, ], }, ), ), }, ], sort_clause: [], limit_offset: None, limit_count: None, limit_option: Default, locking_clause: [], with_clause: None, op: SetopNone, all: false, larg: None, rarg: None, }, ), ), }, ), on_conflict_clause: None, returning_list: [], with_clause: None, r#override: OverridingNotSet, }, ) [crates/parser/src/parse/libpg_query_node.rs:67] &self.node_graph = StableGraph { Ty: "Directed", node_count: 5, edge_count: 4, edges: (0, 1), (0, 2), (2, 3), (3, 4), node weights: { 0: Node { kind: InsertStmt, depth: 1, properties: [ TokenProperty { value: None, kind: Some( Insert, ), }, TokenProperty { value: None, kind: Some( Into, ), }, ], location: None, }, 1: Node { kind: RangeVar, depth: 2, properties: [ TokenProperty { value: Some( "tbl", ), kind: None, }, TokenProperty { value: Some( "p", ), kind: None, }, ], location: Some( 12, ), }, 2: Node { kind: SelectStmt, depth: 2, properties: [ TokenProperty { value: None, kind: Some( Select, ), }, TokenProperty { value: None, kind: Some( Values, ), }, ], location: None, }, 3: Node { kind: List, depth: 3, properties: [], location: None, }, 4: Node { kind: ColumnRef, depth: 4, properties: [ TokenProperty { value: Some( "a", ), kind: None, }, ], location: Some( 24, ), }, }, free_node: NodeIndex(4294967295), free_edge: EdgeIndex(4294967295), } ```
psteinroe commented 9 months ago

It looks like create procedure is parsed as an insert in valid_statements, when sql_body is not $$ dollar-quoted:

this must be fixed in the statement parser. will do so this week!

psteinroe commented 9 months ago

@cvng this is now ready isn't it?

cvng commented 9 months ago

@psteinroe let me rebase and check real quick