DerekStride / tree-sitter-sql

SQL grammar for tree-sitter
http://derek.stride.host/tree-sitter-sql/
MIT License
147 stars 47 forks source link

feat!: Add complex index fields to CREATE INDEX statements #229

Closed dgmcdona closed 9 months ago

dgmcdona commented 9 months ago

See postgresql CREATE INDEX docs

The CREATE INDEX statement should be able to support three different types of index fields:

  1. Column names
  2. Expressions (wrapped in parentheses)
  3. Function calls

This PR adds those features to the CREATE INDEX statement while preserving backwards compatibility with existing tests/queries.

There is some code duplication going on here that I couldn't figure out how to resolve; maybe someone has a suggestion for how this can be improved. Each of the three types of index fields takes a set of optional parameters, but I couldn't break them out into their own rule since they are all optional and a rule containing only optional fields would match the empty string rule and therefore be invalid. I also couldn't wrap the three types in a choice followed by the optional nodes since the optional nodes need to be children of the column node in order to not break existing tests/queries.

dgmcdona commented 9 months ago

This works as well and is a little more concise, but changes the output slightly for the test that I added:

    _index_field: $ => seq(
      choice(
        field("column_expression", wrapped_in_parenthesis($._expression)),
        field("column_function", $.invocation),
        field("name", $._column),
      ),
      optional(seq($.keyword_collate, $.identifier)),
      optional($._operator_class),
      optional($.direction),
      optional(
        seq(
          $.keyword_nulls,
          choice(
            $.keyword_first,
            $.keyword_last
          )
        )
      ),
    ),

    index_fields: $ => wrapped_in_parenthesis(comma_list(alias($._index_field, $.column))),

    create_index: $ => seq(
      $.keyword_create,
      optional($.keyword_unique),
      $.keyword_index,
      optional($.keyword_concurrently),
      optional(
        seq(
          optional($._if_not_exists),
          field("column", $._column),
        ),
      ),
      $.keyword_on,
      optional($.keyword_only),
      seq(
        $.object_reference,
        optional(
          seq(
            $.keyword_using,
            choice(
              $.keyword_btree,
              $.keyword_hash,
              $.keyword_gist,
              $.keyword_spgist,
              $.keyword_gin,
              $.keyword_brin
            ),
          ),
        ),
        alias($.index_fields, $.ordered_columns)
      ),
      optional(
        $.where,
      ),
    ),
dgmcdona commented 9 months ago

Thanks for the feedback! I made those changes and updated the commit message/PR title to reflect breaking changes.

DerekStride commented 9 months ago

I merged this to move things forward but if there's any concerns I can follow up in another PR