From 8a5c5b5412590fe362f652e23c850ae1a45b5349 Mon Sep 17 00:00:00 2001 From: Ted Conbeer Date: Wed, 2 Feb 2022 14:25:58 -0700 Subject: [PATCH] fix: add support for more operators, closes #105 --- CHANGELOG.md | 1 + src/sqlfmt/dialect.py | 38 ++++++++++++++++---- tests/data/errors/900_bad_token.sql | 4 +-- tests/unit_tests/test_api.py | 2 -- tests/unit_tests/test_dialect.py | 56 +++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1ad1261..94b1633a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file. ### Fixes - adds "cross join" to list of supported join types. No longer merges the "cross" keyword with the previous statement ([#110](https://github.com/tconbeer/sqlfmt/issues/110) - thank you [@rdeese](https://github.com/rdeese)!) +- add support for every valid operator in postgresql, even the weird ones, like `@>`, `||/`, `?-|` ([#105](https://github.com/tconbeer/sqlfmt/issues/105)) ## [0.4.3] - 2022-01-31 diff --git a/src/sqlfmt/dialect.py b/src/sqlfmt/dialect.py index 956baba0..aee42cbc 100644 --- a/src/sqlfmt/dialect.py +++ b/src/sqlfmt/dialect.py @@ -220,12 +220,33 @@ def __init__(self) -> None: name="operator", priority=910, pattern=group( + r"\|\|?\/", # square or cube root ||/ + r"~=", # geo compare + r"!?~\*?", # posix like/not like + r"\?(=|!|<=|", # distance operator + r"@>", # contains + r"<@", # contained by r"<>", - r"!=", + r"\|?>>=?", + r"<<(=|\|)?", r"=>", + r"(-|#)>>?", # json extraction + r"&&", + r"&<\|?", # not extends + r"\|?&>", # not extends + r"<\^", # below + r">\^", # above + r"\?#", # intersect r"\|\|", - r"[+\-*/%&@|^=<>:]=?", - r"~", + r"-\|-", + r"[*+?]?\?", # regex greedy/non-greedy, also ? + r"!!", # negate text match + r"[+\-*/%&@|^=<>:#!]=?", # singles ), action=partial( actions.add_node_to_buffer, token_type=TokenType.OPERATOR @@ -235,16 +256,21 @@ def __init__(self) -> None: name="word_operator", priority=920, pattern=group( + r"all", + r"any", r"between", + r"exists", r"ilike", r"in", r"is", r"isnull", - r"like", - r"not", + r"like(\s+any)?", r"notnull", + r"not", r"over", - r"similar", + r"rlike", + r"some", + r"similar\s+to", ) + group(r"\W", r"$"), action=partial( diff --git a/tests/data/errors/900_bad_token.sql b/tests/data/errors/900_bad_token.sql index 1a41c003..bacb79ad 100644 --- a/tests/data/errors/900_bad_token.sql +++ b/tests/data/errors/900_bad_token.sql @@ -1,3 +1 @@ -select ? -from my_table -where no_question_mark is true \ No newline at end of file +select $ diff --git a/tests/unit_tests/test_api.py b/tests/unit_tests/test_api.py index 0902655f..0194d5ee 100644 --- a/tests/unit_tests/test_api.py +++ b/tests/unit_tests/test_api.py @@ -5,7 +5,6 @@ import pytest -from sqlfmt.analyzer import SqlfmtParsingError from sqlfmt.api import ( _format_many, _generate_matched_paths, @@ -51,7 +50,6 @@ def test_format_empty_string(all_output_modes: Mode) -> None: @pytest.mark.parametrize( "source,exception", [ - ("?\n", SqlfmtParsingError), ("select )\n", SqlfmtBracketError), ("{{\n", SqlfmtBracketError), ], diff --git a/tests/unit_tests/test_dialect.py b/tests/unit_tests/test_dialect.py index a4cbecbf..614adba8 100644 --- a/tests/unit_tests/test_dialect.py +++ b/tests/unit_tests/test_dialect.py @@ -82,11 +82,67 @@ def test_rule_props_are_unique(self, polyglot: Polyglot) -> None: ("main", "double_colon", "::"), ("main", "colon", ":"), ("main", "semicolon", ";"), + ("main", "operator", "+"), + ("main", "operator", "-"), + ("main", "operator", "/"), ("main", "operator", "<>"), ("main", "operator", "||"), ("main", "operator", "=>"), + ("main", "operator", "||/"), + ("main", "operator", "|/"), + ("main", "operator", "#"), + ("main", "operator", ">>"), + ("main", "operator", "<<"), + ("main", "operator", "!"), + ("main", "operator", "!="), + # posix like/ not like + ("main", "operator", "~"), + ("main", "operator", "!~"), + ("main", "operator", "~*"), + ("main", "operator", "!~*"), + # postgresql geo operators + # see: https://www.postgresql.org/docs/current/functions-geometry.html + ("main", "operator", "@-@"), + ("main", "operator", "@@"), + ("main", "operator", "##"), + ("main", "operator", "<->"), + ("main", "operator", "<@"), + ("main", "operator", "@>"), + ("main", "operator", "&&"), + ("main", "operator", "&<"), + ("main", "operator", "&>"), + ("main", "operator", "<<|"), + ("main", "operator", "|>>"), + ("main", "operator", "&<|"), + ("main", "operator", "|&>"), + ("main", "operator", "<^"), + ("main", "operator", ">^"), + ("main", "operator", "?#"), + ("main", "operator", "?-"), + ("main", "operator", "?|"), + ("main", "operator", "?-|"), + ("main", "operator", "?||"), + ("main", "operator", "~="), + # network operators + # see https://www.postgresql.org/docs/current/functions-net.html + ("main", "operator", "<<="), + ("main", "operator", ">>="), + # json operators + # see https://www.postgresql.org/docs/current/functions-json.html + ("main", "operator", "->"), + ("main", "operator", "->>"), + ("main", "operator", "#>"), + ("main", "operator", "#>>"), + ("main", "operator", "-|-"), # range adjacency ("main", "word_operator", "is"), ("main", "word_operator", "in"), + ("main", "word_operator", "like"), + ("main", "word_operator", "ilike"), + ("main", "word_operator", "like any"), + ("main", "word_operator", "any"), + ("main", "word_operator", "some"), + ("main", "word_operator", "exists"), + ("main", "word_operator", "all"), ("main", "as", "as"), ("main", "on", "on"), ("main", "boolean_operator", "AND"),