Repository: DerekStride/tree-sitter-sql (language: python)

Issue:
select statements enclosed in parentheses next to 'union' aren't parsed proplerly
Hi, the following query isn't parsed correctly, please have a look.
```
-- CREATE TABLE tb AS
(
  SELECT 1 as col
)
UNION ALL
(
  SELECT 2 as col
);
```




Discussion / hints:
Thanks. Maybe I misled you. It works after a create table statement, but fails alone. The following query is also legal.
"--" stands for comments in sparksql
```
(
  SELECT 1 as col
)
UNION ALL
(
  SELECT 2 as col
);
```
Ah, ok. Yeah I was wondering about that. I will try to fix this later today. I was on the notion that the select with parens should already work. However, the create table statement was also not working, so now it does :-)

`--` is the general comment string in all SQL dialects.
OK, I did take a deeper look into that, and I have to say that it is probably quite difficult to add to the parser in its current state. Adding these parenthesis for `select` statements creates a lot of other problems and conflicts. They can also around CTEs. But we have put CTEs in the node `_dml_statement`, which also includes `insert`, `delete` and `update`. Adding the


So...
```sql
(
  with x as (
      select 1 as a
  )
  select * from x
)
```
is valid, also a valid query.

I am wondering what the parenthesis should do anyway? They do not seem to have any purpose here and I can not think of a use case.
I have checked this against PostgreSQL and it seems to be not documented as well. [SELECT](https://www.postgresql.org/docs/15/sql-select.html) Same is true for MySQL and ANSI SQL - looks like this is not documented.

@DerekStride @dmfay what is your take on this?

I think I found a solution, but it involved adding a lot of precedence and associativity rules. I will create a PR later.

Fix patch:
diff --git a/grammar.js b/grammar.js
index 508c7457..33bbfeeb 100644
--- a/grammar.js
+++ b/grammar.js
@@ -409,11 +409,11 @@ module.exports = grammar({
       $.keyword_regtype,
     ),
 
-    tinyint: $ => unsigned_type($, parametric_type($, $.keyword_tinyint)),
-    smallint: $ => unsigned_type($, parametric_type($, $.keyword_smallint)),
-    mediumint: $ => unsigned_type($, parametric_type($, $.keyword_mediumint)),
-    int: $ => unsigned_type($, parametric_type($, $.keyword_int)),
-    bigint: $ => unsigned_type($, parametric_type($, $.keyword_bigint)),
+    tinyint: $ => prec.left(unsigned_type($, parametric_type($, $.keyword_tinyint))),
+    smallint: $ => prec.left(unsigned_type($, parametric_type($, $.keyword_smallint))),
+    mediumint: $ => prec.left(unsigned_type($, parametric_type($, $.keyword_mediumint))),
+    int: $ => prec.left(unsigned_type($, parametric_type($, $.keyword_int))),
+    bigint: $ => prec.left(unsigned_type($, parametric_type($, $.keyword_bigint))),
 
     bit: $ => choice(
         $.keyword_bit,
@@ -426,15 +426,17 @@ module.exports = grammar({
 
     // TODO: should qualify against /\\b(0?[1-9]|[1-4][0-9]|5[0-4])\\b/g
     float: $  => choice(
-      prec(0, parametric_type($, $.keyword_float, ['precision'])),
-      prec(1, unsigned_type($, parametric_type($, $.keyword_float, ['precision', 'scale']))),
+      prec.right(parametric_type($, $.keyword_float, ['precision'])),
+      prec.right(unsigned_type($, parametric_type($, $.keyword_float, ['precision', 'scale']))),
     ),
 
-    double: $ => choice(
-      make_keyword("float8"),
-      unsigned_type($, parametric_type($, $.keyword_double, ['precision', 'scale'])),
-      unsigned_type($, parametric_type($, seq($.keyword_double, $.keyword_precision), ['precision', 'scale'])),
-      unsigned_type($, parametric_type($, $.keyword_real, ['precision', 'scale'])),
+    double: $ => prec.left(
+      choice(
+        make_keyword("float8"),
+        unsigned_type($, parametric_type($, $.keyword_double, ['precision', 'scale'])),
+        unsigned_type($, parametric_type($, seq($.keyword_double, $.keyword_precision), ['precision', 'scale'])),
+        unsigned_type($, parametric_type($, $.keyword_real, ['precision', 'scale'])),
+      ),
     ),
 
     decimal: $ => choice(
@@ -499,16 +501,32 @@ module.exports = grammar({
     ),
 
     _dml_statement: $ => seq(
+      choice(
+        $._cte_select_variants,
+        seq('(', $._cte_select_variants,')'),
+        seq(
+          optional(
+            $._cte
+          ),
+          choice(
+            $._delete_statement,
+            $._insert_statement,
+            $._update_statement,
+          ),
+        ),
+      ),
+      optional($.window_clause),
+    ),
+
+    _cte_select_variants: $ => prec.left(seq(
       optional(
-          $._cte
+        $._cte
       ),
       choice(
         $._select_statement,
-        $._delete_statement,
-        $._insert_statement,
-        $._update_statement,
-      ),
-      optional($.window_clause),
+        seq('(', $._select_statement, ')'),
+      )
+    ),
     ),
 
     cte: $ => seq(
@@ -1777,9 +1795,11 @@ module.exports = grammar({
       optional($.order_by),
     ),
 
-    invocation: $ => seq(
-      field('name', $.identifier),
-      paren_list(field('parameter', $._select_expression)),
+    invocation: $ => prec(1,
+      seq(
+        field('name', $.identifier),
+        paren_list(field('parameter', $._select_expression)),
+      ),
     ),
 
     exists: $ => seq(
@@ -1927,22 +1947,24 @@ module.exports = grammar({
       optional($.limit),
     ),
 
-    relation: $ => seq(
-      choice(
-        $.subquery,
-        $.invocation,
-        $.table_reference,
-        seq(
-          '(',
-          $.values,
-          ')',
+    relation: $ => prec.right(
+      seq(
+        choice(
+          $.subquery,
+          $.invocation,
+          $.table_reference,
+          seq(
+            '(',
+            $.values,
+            ')',
+          ),
         ),
-      ),
-      optional(
-        seq(
-          optional($.keyword_as),
-          field('table_alias', $._alias_identifier),
-          optional(alias($._column_list, $.list)),
+        optional(
+          seq(
+            optional($.keyword_as),
+            field('table_alias', $._alias_identifier),
+            optional(alias($._column_list, $.list)),
+          ),
         ),
       ),
     ),


Test patch:
diff --git a/test/corpus/cte.txt b/test/corpus/cte.txt
index 3af89cc6..a3f34f7a 100644
--- a/test/corpus/cte.txt
+++ b/test/corpus/cte.txt
@@ -175,3 +175,41 @@ FROM second;
       (relation
         (table_reference
           (identifier))))))
+
+================================================================================
+Parenthesized CTE
+================================================================================
+
+(
+  WITH data AS (
+    SELECT 1 AS col
+  )
+  SELECT *
+  FROM data
+)
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (keyword_with)
+    (cte
+      (identifier)
+      (keyword_as)
+      (statement
+        (select
+          (keyword_select)
+          (select_expression
+            (term
+              (literal)
+              (keyword_as)
+              (identifier))))))
+    (select
+      (keyword_select)
+      (select_expression
+        (all_fields)))
+    (from
+      (keyword_from)
+      (relation
+        (table_reference
+          (identifier))))))
diff --git a/test/corpus/select.txt b/test/corpus/select.txt
index a44f269f..653f6aad 100644
--- a/test/corpus/select.txt
+++ b/test/corpus/select.txt
@@ -2518,3 +2518,19 @@ WHERE (foo OR bar) AND baz
           operator: (keyword_and)
           right: (field
             name: (identifier)))))))
+
+================================================================================
+parenthesized select
+================================================================================
+
+(SELECT 1)
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (select
+      (keyword_select)
+      (select_expression
+        (term
+          (literal))))))