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

Issue:
PG: Support creation and usage of  custom types
Postgres allows to create custom types. The creation of types, tables that are using these types and the selection of columns with these types is not yet supported:

Here is an example:

```sql
CREATE TYPE obj AS (
    elem1 TEXT,
    elem2 INT
);


CREATE TABLE obj_tab (
    object obj
);

SELECT
    (object).elem1,
    (object).elem2,
FROM obj_tab
```

Docs:
https://www.postgresql.org/docs/current/sql-createtype.html


Discussion / hints:

Fix patch:
diff --git a/grammar.js b/grammar.js
index 390bf34a..9c008994 100644
--- a/grammar.js
+++ b/grammar.js
@@ -62,6 +62,7 @@ module.exports = grammar({
     keyword_into: _ => make_keyword("into"),
     keyword_overwrite: _ => make_keyword("overwrite"),
     keyword_values: _ => make_keyword("values"),
+    keyword_value: _ => make_keyword("value"),
     keyword_matched: _ => make_keyword("matched"),
     keyword_set: _ => make_keyword("set"),
     keyword_from: _ => make_keyword("from"),
@@ -161,6 +162,7 @@ module.exports = grammar({
     keyword_nulls: _ => make_keyword("nulls"),
     keyword_first: _ => make_keyword("first"),
     keyword_after: _ => make_keyword("after"),
+    keyword_before: _ => make_keyword("before"),
     keyword_last: _ => make_keyword("last"),
     keyword_window: _ => make_keyword("window"),
     keyword_range: _ => make_keyword("range"),
@@ -206,7 +208,7 @@ module.exports = grammar({
     keyword_vacuum: _ => make_keyword("vacuum"),
     keyword_wait: _ => make_keyword("wait"),
     keyword_nowait: _ => make_keyword("nowait"),
-
+    keyword_attribute: _ => make_keyword("attribute"),
 
     keyword_trigger: _ => make_keyword('trigger'),
     keyword_function: _ => make_keyword("function"),
@@ -440,6 +442,8 @@ module.exports = grammar({
         $.keyword_regnamespace,
         $.keyword_regproc,
         $.keyword_regtype,
+
+        field("custom_type", $._identifier)
       ),
       optional($.array_size_definition)
     ),
@@ -715,6 +719,7 @@ module.exports = grammar({
         $.create_materialized_view,
         $.create_index,
         $.create_function,
+        $.create_type,
         // TODO sequence
       ),
     ),
@@ -1051,10 +1056,52 @@ module.exports = grammar({
       ),
     ),
 
+    create_type: $ => seq(
+      $.keyword_create,
+      $.keyword_type,
+      $.identifier,
+      optional(
+        seq(
+          choice(
+            seq(
+              $.keyword_as,
+              $.column_definitions,
+              optional(seq($.keyword_collate, $.identifier))
+            ),
+            seq(
+              $.keyword_as,
+              $.keyword_enum,
+              $.enum_elements,
+            ),
+            seq(
+              optional(
+                seq(
+                  $.keyword_as,
+                  $.keyword_range,
+                )
+              ),
+              paren_list(
+                seq(
+                  field('name', $.identifier),
+                  '=',
+                  field('value', choice($.identifier,alias($._single_quote_string, $.literal))),
+                ),
+              ),
+            ),
+          ),
+        ),
+      ),
+    ),
+
+    enum_elements: $ => seq(
+      paren_list(field("enum_element", alias($._literal_string, $.literal))),
+    ),
+
     _alter_statement: $ => seq(
       choice(
         $.alter_table,
         $.alter_view,
+        $.alter_type,
       ),
     ),
 
@@ -1241,11 +1288,79 @@ module.exports = grammar({
       ),
     ),
 
+    alter_type: $ => seq(
+      $.keyword_alter,
+      $.keyword_type,
+      $.identifier,
+      choice(
+        $.change_ownership,
+        $.set_schema,
+        $.rename_object,
+        seq(
+          $.keyword_rename,
+          $.keyword_attribute,
+          $.identifier,
+          $.keyword_to,
+          $.identifier,
+          optional($._drop_bevavior)
+        ),
+        seq(
+          $.keyword_add,
+          $.keyword_value,
+          optional($._if_not_exists),
+            alias($._single_quote_string,$.literal),
+          optional(
+            seq(
+              choice($.keyword_before, $.keyword_after),
+              alias($._single_quote_string,$.literal),
+            )
+          ),
+        ),
+        seq(
+          $.keyword_rename,
+          $.keyword_value,
+          alias($._single_quote_string,$.literal),
+          $.keyword_to,
+          alias($._single_quote_string,$.literal),
+        ),
+        seq(
+          choice(
+            seq(
+              $.keyword_add,
+              $.keyword_attribute,
+              $.identifier,
+              $._type
+            ),
+            seq($.keyword_drop,
+              $.keyword_attribute,
+              optional($._if_exists),
+              $.identifier),
+            seq(
+              $.keyword_alter,
+              $.keyword_attribute,
+              $.identifier,
+              optional(seq($.keyword_set, $.keyword_data)),
+              $.keyword_type,
+              $._type
+            ),
+          ),
+          optional(seq($.keyword_collate, $.identifier)),
+          optional($._drop_bevavior)
+        )
+      ),
+    ),
+
+    _drop_bevavior: $ => choice(
+      $.keyword_cascade,
+      $.keyword_restrict,
+    ),
+
     _drop_statement: $ => seq(
       choice(
         $.drop_table,
         $.drop_view,
         $.drop_index,
+        $.drop_type,
       ),
     ),
 
@@ -1255,7 +1370,7 @@ module.exports = grammar({
       optional($._if_exists),
       $.object_reference,
       optional(
-        $.keyword_cascade,
+        $._drop_bevavior
       ),
     ),
 
@@ -1265,7 +1380,17 @@ module.exports = grammar({
       optional($._if_exists),
       $.object_reference,
       optional(
-        $.keyword_cascade,
+        $._drop_bevavior
+      ),
+    ),
+
+    drop_type: $ => seq(
+      $.keyword_drop,
+      $.keyword_type,
+      optional($._if_exists),
+      $.object_reference,
+      optional(
+        $._drop_bevavior
       ),
     ),
 
@@ -1276,10 +1401,7 @@ module.exports = grammar({
       optional($._if_exists),
       field("name", $.identifier),
       optional(
-        choice(
-            $.keyword_cascade,
-            $.keyword_restrict,
-        ),
+        $._drop_bevavior
       ),
       optional(
         seq(
@@ -1874,7 +1996,7 @@ module.exports = grammar({
     _qualified_field: $ => seq(
       optional(
         seq(
-          $.object_reference,
+          optional_parenthesis($.object_reference),
           '.',
         ),
       ),
@@ -2448,11 +2570,12 @@ module.exports = grammar({
       ),
     ),
     _double_quote_string: _ => seq('"', /[^"]*/, '"'),
+    _single_quote_string: _ => seq("'", /([^']|'')*/, "'"),
     _literal_string: $ => prec(1,
-        choice(
-            seq("'", /([^']|'')*/, "'"),
-            $._double_quote_string,
-        ),
+      choice(
+        $._single_quote_string,
+        $._double_quote_string,
+      ),
     ),
     _natural_number: _ => /\d+/,
     _integer: $ => seq(optional("-"), $._natural_number),
diff --git a/queries/highlights.scm b/queries/highlights.scm
index f59fa0d1..745858e2 100644
--- a/queries/highlights.scm
+++ b/queries/highlights.scm
@@ -119,6 +119,8 @@
   (keyword_overwrite)
   (keyword_matched)
   (keyword_values)
+  (keyword_value)
+  (keyword_attribute)
   (keyword_set)
   (keyword_left)
   (keyword_right)
@@ -182,6 +184,7 @@
   (keyword_change)
   (keyword_modify)
   (keyword_after)
+  (keyword_before)
   (keyword_range)
   (keyword_rows)
   (keyword_groups)


Test patch:
diff --git a/test/corpus/custom_types.txt b/test/corpus/custom_types.txt
new file mode 100644
index 00000000..e99e9c5e
--- /dev/null
+++ b/test/corpus/custom_types.txt
@@ -0,0 +1,424 @@
+================================================================================
+CREATE TYPE
+================================================================================
+
+ CREATE TYPE sports
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (create_type
+      (keyword_create)
+      (keyword_type)
+      (identifier))))
+
+================================================================================
+CREATE TYPE ENUM
+================================================================================
+
+CREATE TYPE colors as ENUM (
+    'red',
+    'green',
+    'blue'
+);
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (create_type
+      (keyword_create)
+      (keyword_type)
+      (identifier)
+      (keyword_as)
+      (keyword_enum)
+      (enum_elements
+        (literal)
+        (literal)
+        (literal)))))
+
+================================================================================
+CREATE TYPE Object
+================================================================================
+CREATE TYPE cities AS (
+    cityname text,
+    population INTEGER
+);
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (create_type
+      (keyword_create)
+      (keyword_type)
+      (identifier)
+      (keyword_as)
+      (column_definitions
+        (column_definition
+          (identifier)
+          (keyword_text))
+        (column_definition
+          (identifier)
+          (int
+            (keyword_int)))))))
+
+================================================================================
+CREATE TYPE RANGE
+================================================================================
+
+CREATE TYPE marathon AS RANGE (
+    SUBTYPE = sports
+)
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (create_type
+      (keyword_create)
+      (keyword_type)
+      (identifier)
+      (keyword_as)
+      (keyword_range)
+      (identifier)
+      (identifier))))
+
+================================================================================
+CREATE TYPE IO
+================================================================================
+CREATE TYPE bigobj (
+    INPUT = lo_filein, OUTPUT = lo_fileout,
+    INTERNALLENGTH = VARIABLE
+);
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (create_type
+      (keyword_create)
+      (keyword_type)
+      (identifier)
+      (identifier)
+      (identifier)
+      (identifier)
+      (identifier)
+      (identifier)
+      (identifier))))
+
+================================================================================
+DROP TYPE
+================================================================================
+
+DROP TYPE IF EXISTS boxes CASCADE;
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (drop_type
+      (keyword_drop)
+      (keyword_type)
+      (keyword_if)
+      (keyword_exists)
+      (object_reference
+        (identifier))
+      (keyword_cascade))))
+
+================================================================================
+ALTER TYPE RENAME
+================================================================================
+
+ALTER TYPE boxes RENAME TO cubes;
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (alter_type
+      (keyword_alter)
+      (keyword_type)
+      (identifier)
+      (rename_object
+        (keyword_rename)
+        (keyword_to)
+        (object_reference
+          (identifier))))))
+
+================================================================================
+ALTER TYPE RENAME ATTRIBUTE
+================================================================================
+
+ALTER TYPE boxes
+RENAME ATTRIBUTE width TO height;
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (alter_type
+      (keyword_alter)
+      (keyword_type)
+      (identifier)
+      (keyword_rename)
+      (keyword_attribute)
+      (identifier)
+      (keyword_to)
+      (identifier))))
+
+================================================================================
+ALTER TYPE CHANGE OWNER
+================================================================================
+
+ALTER TYPE boxes
+OWNER TO user2
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (alter_type
+      (keyword_alter)
+      (keyword_type)
+      (identifier)
+      (change_ownership
+        (keyword_owner)
+        (keyword_to)
+        (identifier)))))
+
+================================================================================
+ALTER TYPE CHANGE SCHEMA
+================================================================================
+
+ALTER TYPE boxes
+SET SCHEMA new_schema
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (alter_type
+      (keyword_alter)
+      (keyword_type)
+      (identifier)
+      (set_schema
+        (keyword_set)
+        (keyword_schema)
+        (identifier)))))
+
+================================================================================
+ALTER TYPE ADD VALUE
+================================================================================
+
+ALTER TYPE boxes
+ADD VALUE  IF NOT EXISTS 'color' AFTER 'weight'
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (alter_type
+      (keyword_alter)
+      (keyword_type)
+      (identifier)
+      (keyword_add)
+      (keyword_value)
+      (keyword_if)
+      (keyword_not)
+      (keyword_exists)
+      (literal)
+      (keyword_after)
+      (literal))))
+
+================================================================================
+ALTER TYPE RENAME VALUE
+================================================================================
+
+ALTER TYPE boxes
+RENAME VALUE 'weight' TO 'mass'
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (alter_type
+      (keyword_alter)
+      (keyword_type)
+      (identifier)
+      (keyword_rename)
+      (keyword_value)
+      (literal)
+      (keyword_to)
+      (literal))))
+
+================================================================================
+ALTER TYPE ADD ATTRIBUTE
+================================================================================
+
+ALTER TYPE boxes
+ADD ATTRIBUTE label text
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (alter_type
+      (keyword_alter)
+      (keyword_type)
+      (identifier)
+      (keyword_add)
+      (keyword_attribute)
+      (identifier)
+      (keyword_text))))
+
+================================================================================
+ALTER TYPE DROP ATTRIBUTE
+================================================================================
+
+ALTER TYPE boxes DROP ATTRIBUTE IF EXISTS label
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (alter_type
+      (keyword_alter)
+      (keyword_type)
+      (identifier)
+      (keyword_drop)
+      (keyword_attribute)
+      (keyword_if)
+      (keyword_exists)
+      (identifier))))
+
+================================================================================
+ALTER TYPE ALTER ATTRIBUTE
+================================================================================
+
+ALTER TYPE boxes
+ADD ATTRIBUTE label varchar(255)
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (alter_type
+      (keyword_alter)
+      (keyword_type)
+      (identifier)
+      (keyword_add)
+      (keyword_attribute)
+      (identifier)
+      (varchar
+        (keyword_varchar)
+        (literal)))))
+
+================================================================================
+CREATE TABLE WITH CUSTOM TYPE
+================================================================================
+
+CREATE TABLE shipments (
+shipment boxes
+)
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (create_table
+      (keyword_create)
+      (keyword_table)
+      (object_reference
+        (identifier))
+      (column_definitions
+        (column_definition
+          (identifier))))))
+
+================================================================================
+INSERT INTO TABLE WITH CUSTOM TYPE
+================================================================================
+
+INSERT INTO shipments (shipment)
+VALUES (ROW(10, 500, 'Box1')),
+       (ROW(15, 800, 'Box2')),
+       (ROW(12, 600, 'Box3'));
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (insert
+      (keyword_insert)
+      (keyword_into)
+      (object_reference
+        (identifier))
+      (list
+        (column
+          (identifier)))
+      (keyword_values)
+      (list
+        (invocation
+          (object_reference
+            (identifier))
+          (term
+            (literal))
+          (term
+            (literal))
+          (term
+            (literal))))
+      (list
+        (invocation
+          (object_reference
+            (identifier))
+          (term
+            (literal))
+          (term
+            (literal))
+          (term
+            (literal))))
+      (list
+        (invocation
+          (object_reference
+            (identifier))
+          (term
+            (literal))
+          (term
+            (literal))
+          (term
+            (literal)))))))
+
+================================================================================
+SELECT FROM CUSTOM TYPE
+================================================================================
+
+SELECT
+    (boxes).height,
+    (boxes).mass,
+    (boxes).label
+FROM shipments
+
+--------------------------------------------------------------------------------
+
+(program
+  (statement
+    (select
+      (keyword_select)
+      (select_expression
+        (term
+          (field
+            (object_reference
+              (identifier))
+            (identifier)))
+        (term
+          (field
+            (object_reference
+              (identifier))
+            (identifier)))
+        (term
+          (field
+            (object_reference
+              (identifier))
+            (identifier)))))
+    (from
+      (keyword_from)
+      (relation
+        (object_reference
+          (identifier))))))