From d4992748eb840d045f90ad638c2145d248063545 Mon Sep 17 00:00:00 2001 From: Gregor Karetka Date: Wed, 20 Dec 2023 15:07:42 +0100 Subject: [PATCH 1/2] Move tests from unstructured to pytest - Move tests to pytest --- pyproject.toml | 2 +- src/duckberg/__init__.py | 16 +++++----- src/duckberg/sqlparser.py | 4 +-- src/duckberg/table.py | 4 +-- tests/duckberg-sample.py | 37 ---------------------- tests/sqlparser/basic_selects.py | 40 ------------------------ tests/sqlparser/test_selects.py | 53 ++++++++++++++++++++++++++++++++ tests/sqlparser/test_where.py | 41 ++++++++++++++++++++++++ tests/sqlparser/where_selects.py | 32 ------------------- tests/test_duckberg.py | 48 +++++++++++++++++++++++++++++ 10 files changed, 155 insertions(+), 122 deletions(-) delete mode 100644 tests/duckberg-sample.py delete mode 100644 tests/sqlparser/basic_selects.py create mode 100644 tests/sqlparser/test_selects.py create mode 100644 tests/sqlparser/test_where.py delete mode 100644 tests/sqlparser/where_selects.py create mode 100644 tests/test_duckberg.py diff --git a/pyproject.toml b/pyproject.toml index 5abf88b..9be3d11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ dependencies = [ "pytest", ] [tool.hatch.envs.default.scripts] -test = "pytest {args:tests}" +test = "pytest ./tests" test-cov = "coverage run -m pytest {args:tests}" cov-report = [ "- coverage combine", diff --git a/src/duckberg/__init__.py b/src/duckberg/__init__.py index a4d727d..574253c 100644 --- a/src/duckberg/__init__.py +++ b/src/duckberg/__init__.py @@ -1,12 +1,13 @@ """Module containing services needed for executing queries with Duckdb + Iceberg.""" from typing import Optional + import duckdb from pyarrow.lib import RecordBatchReader from pyiceberg.catalog import Catalog, load_catalog, load_rest -from duckberg.exceptions import TableNotInCatalogException from pyiceberg.expressions import AlwaysTrue +from duckberg.exceptions import TableNotInCatalogException from duckberg.sqlparser import DuckBergSQLParser from duckberg.table import DuckBergTable, TableWithAlias @@ -70,7 +71,9 @@ def list_partitions(self, table: str): return t.partitions - def select(self, sql: str, table: str = None, partition_filter: str = None, sql_params: [str] = None) -> RecordBatchReader: + def select( + self, sql: str, table: str = None, partition_filter: str = None, sql_params: [str] = None + ) -> RecordBatchReader: if table is not None and partition_filter is not None: return self._select_old(sql, table, partition_filter, sql_params) @@ -94,12 +97,9 @@ def select(self, sql: str, table: str = None, partition_filter: str = None, sql_ if sql_params is None: return self.duckdb_connection.execute(sql).fetch_record_batch(self.batch_size_rows) else: - return ( - self.duckdb_connection.execute(sql, parameters=sql_params) - .fetch_record_batch(self.batch_size_rows) - ) - - def _select_old(self, sql: str, table: str, partition_filter: str, sql_params: [str] = None): + return self.duckdb_connection.execute(sql, parameters=sql_params).fetch_record_batch(self.batch_size_rows) + + def _select_old(self, sql: str, table: str, partition_filter: str, sql_params: [str] = None): table_data_scan_as_arrow = self.tables[table].scan(row_filter=partition_filter).to_arrow() self.duckdb_connection.register(table, table_data_scan_as_arrow) diff --git a/src/duckberg/sqlparser.py b/src/duckberg/sqlparser.py index 82b7822..02a70a6 100644 --- a/src/duckberg/sqlparser.py +++ b/src/duckberg/sqlparser.py @@ -1,9 +1,9 @@ import sqlparse - -from duckberg.table import TableWithAlias from pyiceberg.expressions import * from pyiceberg.expressions import parser +from duckberg.table import TableWithAlias + class DuckBergSQLParser: def parse_first_query(self, sql: str) -> sqlparse.sql.Statement: diff --git a/src/duckberg/table.py b/src/duckberg/table.py index 13ecac1..b38e78f 100644 --- a/src/duckberg/table.py +++ b/src/duckberg/table.py @@ -1,10 +1,10 @@ +import sqlparse from pyiceberg.catalog import Catalog +from pyiceberg.expressions import BooleanExpression from pyiceberg.io import FileIO from pyiceberg.table import Table from pyiceberg.table.metadata import TableMetadata from pyiceberg.typedef import Identifier -from pyiceberg.expressions import BooleanExpression -import sqlparse class DuckBergTable(Table): diff --git a/tests/duckberg-sample.py b/tests/duckberg-sample.py deleted file mode 100644 index 709c09d..0000000 --- a/tests/duckberg-sample.py +++ /dev/null @@ -1,37 +0,0 @@ -from duckberg import DuckBerg - -MINIO_URI = "http://localhost:9000/" -MINIO_USER = "admin" -MINIO_PASSWORD = "password" - -catalog_config: dict[str, str] = { - "type": "rest", - "uri": "http://localhost:8181/", - "credentials": "admin:password", - "s3.endpoint": MINIO_URI, - "s3.access-key-id": MINIO_USER, - "s3.secret-access-key": MINIO_PASSWORD, -} - -catalog_name = "warehouse" - -db = DuckBerg(catalog_name=catalog_name, catalog_config=catalog_config) - -tables = db.list_tables() - -assert(len(tables) == 1) - -# New way of quering data without partition filter -query: str = "SELECT count(*) FROM (SELECT * FROM 'nyc.taxis' WHERE trip_distance > 40 ORDER BY tolls_amount DESC)" -df = db.select(sql=query).read_pandas() -assert(df['count_star()'][0] == 2614) - -# New way of quering data -query: str = "SELECT count(*) FROM (SELECT * FROM 'nyc.taxis' WHERE payment_type = 1 AND trip_distance > 40 ORDER BY tolls_amount DESC)" -df = db.select(sql=query).read_pandas() -assert(df['count_star()'][0] == 1673) - -# Old way of quering data -query: str = "SELECT count(*) FROM (SELECT * FROM 'nyc.taxis' WHERE payment_type = 1 AND trip_distance > 40 ORDER BY tolls_amount DESC)" -df = db.select(sql=query, table="nyc.taxis", partition_filter="payment_type = 1").read_pandas() -assert(df['count_star()'][0] == 1673) diff --git a/tests/sqlparser/basic_selects.py b/tests/sqlparser/basic_selects.py deleted file mode 100644 index 53efc1a..0000000 --- a/tests/sqlparser/basic_selects.py +++ /dev/null @@ -1,40 +0,0 @@ -from duckberg.sqlparser import DuckBergSQLParser - - -parser = DuckBergSQLParser() - - -sql1 = """ -SELECT * FROM this_is_awesome_table""" -sql1_parsed = parser.parse_first_query(sql=sql1) -res1 = parser.extract_tables(sql1_parsed) -assert len(res1) == 1 -assert list(map(lambda x: str(x), res1)) == ["this_is_awesome_table (None)"] - -sql2 = """ -SELECT * FROM this_is_awesome_table, second_awesome_table""" -sql2_parsed = parser.parse_first_query(sql=sql2) -res2 = parser.extract_tables(sql2_parsed) -assert len(res2) == 2 -assert list(map(lambda x: str(x), res2)) == ["this_is_awesome_table (None)", "second_awesome_table (None)"] - -sql3 = """ -SELECT * FROM (SELECT * FROM (SELECT * FROM this_is_awesome_table))""" -sql3_parsed = parser.parse_first_query(sql=sql3) -res3 = parser.extract_tables(sql3_parsed) -assert len(res3) == 1 -assert list(map(lambda x: str(x), res3)) == ["this_is_awesome_table (None)"] - -sql4 = """ -SELECT * FROM (SELECT * FROM (SELECT * FROM this_is_awesome_table), second_awesome_table)""" -sql4_parsed = parser.parse_first_query(sql=sql4) -res4 = parser.extract_tables(sql4_parsed) -assert len(res4) == 2 -assert list(map(lambda x: str(x), res4)) == ["this_is_awesome_table (None)", "second_awesome_table (None)"] - -sql5 = """ -SELECT * FROM (SELECT * FROM (SELECT * FROM this_is_awesome_table tiat, second_awesome_table))""" -sql5_parsed = parser.parse_first_query(sql=sql5) -res5 = parser.extract_tables(sql5_parsed) -assert len(res5) == 2 -assert list(map(lambda x: str(x), res5)) == ["this_is_awesome_table (tiat)", "second_awesome_table (None)"] diff --git a/tests/sqlparser/test_selects.py b/tests/sqlparser/test_selects.py new file mode 100644 index 0000000..08cbd83 --- /dev/null +++ b/tests/sqlparser/test_selects.py @@ -0,0 +1,53 @@ +import pytest + +from duckberg.sqlparser import DuckBergSQLParser + + +@pytest.fixture +def get_parser(): + return DuckBergSQLParser() + + +def test_basic_select_1(get_parser): + sql1 = """ + SELECT * FROM this_is_awesome_table""" + sql1_parsed = get_parser.parse_first_query(sql=sql1) + res1 = get_parser.extract_tables(sql1_parsed) + assert len(res1) == 1 + assert list(map(lambda x: str(x), res1)) == ["this_is_awesome_table (None)"] + + +def test_basic_select_2(get_parser): + sql2 = """ + SELECT * FROM this_is_awesome_table, second_awesome_table""" + sql2_parsed = get_parser.parse_first_query(sql=sql2) + res2 = get_parser.extract_tables(sql2_parsed) + assert len(res2) == 2 + assert list(map(lambda x: str(x), res2)) == ["this_is_awesome_table (None)", "second_awesome_table (None)"] + + +def test_basic_select_3(get_parser): + sql3 = """ + SELECT * FROM (SELECT * FROM (SELECT * FROM this_is_awesome_table))""" + sql3_parsed = get_parser.parse_first_query(sql=sql3) + res3 = get_parser.extract_tables(sql3_parsed) + assert len(res3) == 1 + assert list(map(lambda x: str(x), res3)) == ["this_is_awesome_table (None)"] + + +def test_basic_select_4(get_parser): + sql4 = """ + SELECT * FROM (SELECT * FROM (SELECT * FROM this_is_awesome_table), second_awesome_table)""" + sql4_parsed = get_parser.parse_first_query(sql=sql4) + res4 = get_parser.extract_tables(sql4_parsed) + assert len(res4) == 2 + assert list(map(lambda x: str(x), res4)) == ["this_is_awesome_table (None)", "second_awesome_table (None)"] + + +def test_basic_select_5(get_parser): + sql5 = """ + SELECT * FROM (SELECT * FROM (SELECT * FROM this_is_awesome_table tiat, second_awesome_table))""" + sql5_parsed = get_parser.parse_first_query(sql=sql5) + res5 = get_parser.extract_tables(sql5_parsed) + assert len(res5) == 2 + assert list(map(lambda x: str(x), res5)) == ["this_is_awesome_table (tiat)", "second_awesome_table (None)"] diff --git a/tests/sqlparser/test_where.py b/tests/sqlparser/test_where.py new file mode 100644 index 0000000..109d267 --- /dev/null +++ b/tests/sqlparser/test_where.py @@ -0,0 +1,41 @@ +import pytest + +from duckberg.sqlparser import DuckBergSQLParser + + +@pytest.fixture +def get_parser(): + return DuckBergSQLParser() + + +def test_select_where_1(get_parser): + sql1 = """ + SELECT * FROM this_is_awesome_table WHERE a > 15""" + sql1_parsed = get_parser.parse_first_query(sql=sql1) + res1 = get_parser.extract_tables(sql1_parsed) + res1_where = str(res1[0].comparisons) + assert "GreaterThan(term=Reference(name='a'), literal=LongLiteral(15))" == res1_where + + +def test_select_where_2(get_parser): + sql2 = """ + SELECT * FROM this_is_awesome_table WHERE a > 15 AND a < 16""" + sql2_parsed = get_parser.parse_first_query(sql=sql2) + res2 = get_parser.extract_tables(sql2_parsed) + res2_where = str(res2[0].comparisons) + assert ( + "And(left=GreaterThan(term=Reference(name='a'), literal=LongLiteral(15)), right=LessThan(term=Reference(name='a'), literal=LongLiteral(16)))" + == res2_where + ) + + +def test_select_where_3(get_parser): + sql3 = """ + SELECT * FROM this_is_awesome_table WHERE (a > 15 AND a < 16) OR c > 15""" + sql3_parsed = get_parser.parse_first_query(sql=sql3) + res3 = get_parser.extract_tables(sql3_parsed) + res3_where = str(res3[0].comparisons) + assert ( + "Or(left=And(left=GreaterThan(term=Reference(name='a'), literal=LongLiteral(15)), right=LessThan(term=Reference(name='a'), literal=LongLiteral(16))), right=GreaterThan(term=Reference(name='c'), literal=LongLiteral(15)))" + == res3_where + ) diff --git a/tests/sqlparser/where_selects.py b/tests/sqlparser/where_selects.py deleted file mode 100644 index 25d39c0..0000000 --- a/tests/sqlparser/where_selects.py +++ /dev/null @@ -1,32 +0,0 @@ -from duckberg.sqlparser import DuckBergSQLParser - - -parser = DuckBergSQLParser() - - -sql1 = """ -SELECT * FROM this_is_awesome_table WHERE a > 15""" -sql1_parsed = parser.parse_first_query(sql=sql1) -res1 = parser.extract_tables(sql1_parsed) -res1_where = str(res1[0].comparisons) -assert "GreaterThan(term=Reference(name='a'), literal=LongLiteral(15))" == res1_where - -sql2 = """ -SELECT * FROM this_is_awesome_table WHERE a > 15 AND a < 16""" -sql2_parsed = parser.parse_first_query(sql=sql2) -res2 = parser.extract_tables(sql2_parsed) -res2_where = str(res2[0].comparisons) -assert ( - "And(left=GreaterThan(term=Reference(name='a'), literal=LongLiteral(15)), right=LessThan(term=Reference(name='a'), literal=LongLiteral(16)))" - == res2_where -) - -sql3 = """ -SELECT * FROM this_is_awesome_table WHERE (a > 15 AND a < 16) OR c > 15""" -sql3_parsed = parser.parse_first_query(sql=sql3) -res3 = parser.extract_tables(sql3_parsed) -res3_where = str(res3[0].comparisons) -assert ( - "Or(left=And(left=GreaterThan(term=Reference(name='a'), literal=LongLiteral(15)), right=LessThan(term=Reference(name='a'), literal=LongLiteral(16))), right=GreaterThan(term=Reference(name='c'), literal=LongLiteral(15)))" - == res3_where -) diff --git a/tests/test_duckberg.py b/tests/test_duckberg.py new file mode 100644 index 0000000..0904278 --- /dev/null +++ b/tests/test_duckberg.py @@ -0,0 +1,48 @@ +import pytest + +from duckberg import DuckBerg + + +@pytest.fixture +def get_duckberg() -> DuckBerg: + MINIO_URI = "http://localhost:9000/" + MINIO_USER = "admin" + MINIO_PASSWORD = "password" + + catalog_config: dict[str, str] = { + "type": "rest", + "uri": "http://localhost:8181/", + "credentials": "admin:password", + "s3.endpoint": MINIO_URI, + "s3.access-key-id": MINIO_USER, + "s3.secret-access-key": MINIO_PASSWORD, + } + + catalog_name = "warehouse" + return DuckBerg(catalog_name=catalog_name, catalog_config=catalog_config) + + +def test_list_tables(get_duckberg): + tables = get_duckberg.list_tables() + assert len(tables) == 1 + + +def test_select_1(get_duckberg): + # New way of quering data without partition filter + query: str = "SELECT count(*) FROM (SELECT * FROM 'nyc.taxis' WHERE trip_distance > 40 ORDER BY tolls_amount DESC)" + df = get_duckberg.select(sql=query).read_pandas() + assert df["count_star()"][0] == 2614 + + +def test_select_2(get_duckberg): + # New way of quering data + query: str = "SELECT count(*) FROM (SELECT * FROM 'nyc.taxis' WHERE payment_type = 1 AND trip_distance > 40 ORDER BY tolls_amount DESC)" + df = get_duckberg.select(sql=query).read_pandas() + assert df["count_star()"][0] == 1673 + + +def test_select_3(get_duckberg): + # Old way of quering data + query: str = "SELECT count(*) FROM (SELECT * FROM 'nyc.taxis' WHERE payment_type = 1 AND trip_distance > 40 ORDER BY tolls_amount DESC)" + df = get_duckberg.select(sql=query, table="nyc.taxis", partition_filter="payment_type = 1").read_pandas() + assert df["count_star()"][0] == 1673 From e84f6f77356cde37174949642fcb03a46a3af0ed Mon Sep 17 00:00:00 2001 From: Gregor Karetka Date: Thu, 21 Dec 2023 12:03:14 +0100 Subject: [PATCH 2/2] Bugfix #5 - string in where not working - String in WHERE statement not working - Update tests --- src/duckberg/sqlparser.py | 3 +- tests/sqlparser/test_selects.py | 67 ++++++++++++++++++--------------- tests/sqlparser/test_where.py | 55 ++++++++++++++++++--------- 3 files changed, 75 insertions(+), 50 deletions(-) diff --git a/src/duckberg/sqlparser.py b/src/duckberg/sqlparser.py index 02a70a6..8483952 100644 --- a/src/duckberg/sqlparser.py +++ b/src/duckberg/sqlparser.py @@ -46,4 +46,5 @@ def extract_tables(self, parsed_sql: sqlparse.sql.Statement) -> list[TableWithAl def extract_where_conditions(self, where_statement: list[sqlparse.sql.Where]): comparison = sqlparse.sql.TokenList(where_statement[1:]) - return parser.parse(str(comparison)) + where_condition = str(comparison).replace('"', "'") # revert from double to single + return parser.parse(where_condition) diff --git a/tests/sqlparser/test_selects.py b/tests/sqlparser/test_selects.py index 08cbd83..f954e74 100644 --- a/tests/sqlparser/test_selects.py +++ b/tests/sqlparser/test_selects.py @@ -4,50 +4,55 @@ @pytest.fixture -def get_parser(): +def get_parser() -> DuckBergSQLParser: return DuckBergSQLParser() def test_basic_select_1(get_parser): - sql1 = """ - SELECT * FROM this_is_awesome_table""" - sql1_parsed = get_parser.parse_first_query(sql=sql1) - res1 = get_parser.extract_tables(sql1_parsed) - assert len(res1) == 1 - assert list(map(lambda x: str(x), res1)) == ["this_is_awesome_table (None)"] + sql = """SELECT * FROM this_is_awesome_table""" + sql_parsed = get_parser.parse_first_query(sql=sql) + res = get_parser.extract_tables(sql_parsed) + assert len(res) == 1 + assert list(map(lambda x: str(x), res)) == ["this_is_awesome_table (None)"] def test_basic_select_2(get_parser): - sql2 = """ - SELECT * FROM this_is_awesome_table, second_awesome_table""" - sql2_parsed = get_parser.parse_first_query(sql=sql2) - res2 = get_parser.extract_tables(sql2_parsed) - assert len(res2) == 2 - assert list(map(lambda x: str(x), res2)) == ["this_is_awesome_table (None)", "second_awesome_table (None)"] + sql = """SELECT * FROM 'this_is_awesome_table'""" + sql_parsed = get_parser.parse_first_query(sql=sql) + print(str(sql_parsed.tokens)) + res = get_parser.extract_tables(sql_parsed) + print(res) + assert len(res) == 1 + assert list(map(lambda x: str(x), res)) == ["this_is_awesome_table (None)"] def test_basic_select_3(get_parser): - sql3 = """ - SELECT * FROM (SELECT * FROM (SELECT * FROM this_is_awesome_table))""" - sql3_parsed = get_parser.parse_first_query(sql=sql3) - res3 = get_parser.extract_tables(sql3_parsed) - assert len(res3) == 1 - assert list(map(lambda x: str(x), res3)) == ["this_is_awesome_table (None)"] + sql = """SELECT * FROM this_is_awesome_table, second_awesome_table""" + sql_parsed = get_parser.parse_first_query(sql=sql) + res = get_parser.extract_tables(sql_parsed) + assert len(res) == 2 + assert list(map(lambda x: str(x), res)) == ["this_is_awesome_table (None)", "second_awesome_table (None)"] def test_basic_select_4(get_parser): - sql4 = """ - SELECT * FROM (SELECT * FROM (SELECT * FROM this_is_awesome_table), second_awesome_table)""" - sql4_parsed = get_parser.parse_first_query(sql=sql4) - res4 = get_parser.extract_tables(sql4_parsed) - assert len(res4) == 2 - assert list(map(lambda x: str(x), res4)) == ["this_is_awesome_table (None)", "second_awesome_table (None)"] + sql = """SELECT * FROM (SELECT * FROM (SELECT * FROM this_is_awesome_table))""" + sql_parsed = get_parser.parse_first_query(sql=sql) + res = get_parser.extract_tables(sql_parsed) + assert len(res) == 1 + assert list(map(lambda x: str(x), res)) == ["this_is_awesome_table (None)"] def test_basic_select_5(get_parser): - sql5 = """ - SELECT * FROM (SELECT * FROM (SELECT * FROM this_is_awesome_table tiat, second_awesome_table))""" - sql5_parsed = get_parser.parse_first_query(sql=sql5) - res5 = get_parser.extract_tables(sql5_parsed) - assert len(res5) == 2 - assert list(map(lambda x: str(x), res5)) == ["this_is_awesome_table (tiat)", "second_awesome_table (None)"] + sql = """SELECT * FROM (SELECT * FROM (SELECT * FROM this_is_awesome_table), second_awesome_table)""" + sql_parsed = get_parser.parse_first_query(sql=sql) + res = get_parser.extract_tables(sql_parsed) + assert len(res) == 2 + assert list(map(lambda x: str(x), res)) == ["this_is_awesome_table (None)", "second_awesome_table (None)"] + + +def test_basic_select_6(get_parser): + sql = """SELECT * FROM (SELECT * FROM (SELECT * FROM this_is_awesome_table tiat, second_awesome_table))""" + sql_parsed = get_parser.parse_first_query(sql=sql) + res = get_parser.extract_tables(sql_parsed) + assert len(res) == 2 + assert list(map(lambda x: str(x), res)) == ["this_is_awesome_table (tiat)", "second_awesome_table (None)"] diff --git a/tests/sqlparser/test_where.py b/tests/sqlparser/test_where.py index 109d267..a88d4c0 100644 --- a/tests/sqlparser/test_where.py +++ b/tests/sqlparser/test_where.py @@ -9,33 +9,52 @@ def get_parser(): def test_select_where_1(get_parser): - sql1 = """ - SELECT * FROM this_is_awesome_table WHERE a > 15""" - sql1_parsed = get_parser.parse_first_query(sql=sql1) - res1 = get_parser.extract_tables(sql1_parsed) - res1_where = str(res1[0].comparisons) - assert "GreaterThan(term=Reference(name='a'), literal=LongLiteral(15))" == res1_where + sql = """SELECT * FROM this_is_awesome_table WHERE a > 15""" + sql_parsed = get_parser.parse_first_query(sql=sql) + res = get_parser.extract_tables(sql_parsed) + res_where = str(res[0].comparisons) + assert "GreaterThan(term=Reference(name='a'), literal=LongLiteral(15))" == res_where def test_select_where_2(get_parser): - sql2 = """ - SELECT * FROM this_is_awesome_table WHERE a > 15 AND a < 16""" - sql2_parsed = get_parser.parse_first_query(sql=sql2) - res2 = get_parser.extract_tables(sql2_parsed) - res2_where = str(res2[0].comparisons) + sql = """SELECT * FROM this_is_awesome_table WHERE a > 15 AND a < 16""" + sql_parsed = get_parser.parse_first_query(sql=sql) + res = get_parser.extract_tables(sql_parsed) + res_where = str(res[0].comparisons) assert ( "And(left=GreaterThan(term=Reference(name='a'), literal=LongLiteral(15)), right=LessThan(term=Reference(name='a'), literal=LongLiteral(16)))" - == res2_where + == res_where ) def test_select_where_3(get_parser): - sql3 = """ - SELECT * FROM this_is_awesome_table WHERE (a > 15 AND a < 16) OR c > 15""" - sql3_parsed = get_parser.parse_first_query(sql=sql3) - res3 = get_parser.extract_tables(sql3_parsed) - res3_where = str(res3[0].comparisons) + sql = """SELECT * FROM this_is_awesome_table WHERE (a > 15 AND a < 16) OR c > 15""" + sql_parsed = get_parser.parse_first_query(sql=sql) + res = get_parser.extract_tables(sql_parsed) + res_where = str(res[0].comparisons) assert ( "Or(left=And(left=GreaterThan(term=Reference(name='a'), literal=LongLiteral(15)), right=LessThan(term=Reference(name='a'), literal=LongLiteral(16))), right=GreaterThan(term=Reference(name='c'), literal=LongLiteral(15)))" - == res3_where + == res_where + ) + + +def test_select_where_4(get_parser): + sql = """SELECT * FROM this_is_awesome_table WHERE (b = "test string" AND a < 16) OR c > 15""" + sql_parsed = get_parser.parse_first_query(sql=sql) + res = get_parser.extract_tables(sql_parsed) + res_where = str(res[0].comparisons) + assert ( + "Or(left=And(left=EqualTo(term=Reference(name='b'), literal=literal('test string')), right=LessThan(term=Reference(name='a'), literal=LongLiteral(16))), right=GreaterThan(term=Reference(name='c'), literal=LongLiteral(15)))" + == res_where + ) + + +def test_select_where_4(get_parser): + sql = """SELECT * FROM this_is_awesome_table WHERE (b = "test string" AND column = '108e6307-f23a-4e10-9e38-1866d58b4355') OR c > 15""" + sql_parsed = get_parser.parse_first_query(sql=sql) + res = get_parser.extract_tables(sql_parsed) + res_where = str(res[0].comparisons) + assert ( + "Or(left=And(left=EqualTo(term=Reference(name='b'), literal=literal('test string')), right=EqualTo(term=Reference(name='column'), literal=literal('108e6307-f23a-4e10-9e38-1866d58b4355'))), right=GreaterThan(term=Reference(name='c'), literal=LongLiteral(15)))" + == res_where )