diff --git a/README.md b/README.md index ade2ab4..008b137 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ Right now, the focus is on building a command-line tool that follows these core SELECT [ DISTINCT | PARTIALS ] [ * | python_expression [ AS output_column_name ] [, ...] ] [ FROM csv | spy | text | python_expression | json [ EXPLODE path ] ] - [ WHERE python_expression ] + [ WHERE python_expression [ [NOT] LIKE string] ] [ GROUP BY output_column_number | python_expression [, ...] ] [ ORDER BY output_column_number | python_expression [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] ] diff --git a/spyql/cli.py b/spyql/cli.py index 9538859..d7a657f 100644 --- a/spyql/cli.py +++ b/spyql/cli.py @@ -207,6 +207,42 @@ def parse_select(sel, strings): return res, has_distinct, has_partials +def parse_wherelike(clause, strings): + """splits the LIKE clause and completely supports the SQL syntax + https://docs.microsoft.com/en-us/sql/t-sql/language-elements/like-transact-sql?view=sql-server-ver15""" + # We're not in a LIKE expression, do nothing + if not re.search("LIKE", clause): + return clause + + # Supports words containing [a-zA-Z0-9_\-] + expr_pattern = re.compile(r"([\w-]+)(?:\s+(NOT))?\s+LIKE\s+([\w-]+)", re.IGNORECASE) + groups = re.search(expr_pattern, clause) + if groups is None: + spyql.log.user_error( + f"{clause}", + SyntaxError("unexpected EOF while parsing") + ) + + groups = groups.groups() + negate = "NOT" in {groups[1]} # placed within {} because it can be None + + if not groups[2] in strings: + spyql.log.user_error( + f"{groups[2]}: missing quotes, must be a string", + SyntaxError("bad query") + ) + + # Replacing SQL wildcard '%' for regex wildcard '.*' if not preceded by '\' + pattern = strings.put_strings_back(groups[2]) + pattern = re.compile(r"(? 0", SyntaxError) + exception_test("SELECT * from range(3) WHERE col1 LIKE 1", SyntaxError) + exception_test("SELECT * from range(3) WHERE col1 LIKE", SyntaxError) def test_sql_output():