Skip to content

Commit 21b77c4

Browse files
committed
add support for '0=contains()' in ADQL
1 parent 7d0c751 commit 21b77c4

File tree

5 files changed

+33
-63
lines changed

5 files changed

+33
-63
lines changed

src/queryparser/adql/ADQLParser.g4

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ comp_op: EQ | NOT_EQ | LTH | GTH | GRET | LEET ;
4141
comparison_predicate: value_expression comp_op value_expression ;
4242
concatenation_operator: CONCAT ;
4343
contains: CONTAINS LPAREN geometry_value_expression COMMA geometry_value_expression RPAREN ;
44+
contains_predicate: INT EQ contains;
4445
coord_sys: string_value_expression ;
4546
coord_value: point_value | column_reference ;
4647
coord1: COORD1 LPAREN coord_value RPAREN ;
@@ -136,7 +137,8 @@ polygon: POLYGON LPAREN
136137
coordinates COMMA
137138
coordinates ( COMMA coordinates )+ RPAREN ;
138139
predicate:
139-
comparison_predicate
140+
contains_predicate
141+
| comparison_predicate
140142
| between_predicate
141143
| in_predicate
142144
| like_predicate

src/queryparser/adql/adqltranslator.py

+17-26
Original file line numberDiff line numberDiff line change
@@ -299,29 +299,6 @@ def visitPolygon(self, ctx):
299299
self.contexts[ctx] = ctx_text
300300

301301

302-
class ADQLContainsVisitor(ADQLParserVisitor):
303-
def __init__(self):
304-
self.contains = []
305-
306-
def visitContains(self, ctx):
307-
if ctx.getText().lower()[:8] == 'contains':
308-
self.contains.append(ctx)
309-
310-
311-
class ADQLComparisonPredicateVisitor(ADQLParserVisitor):
312-
# Get rid of "1=" in 1=CONTAINS() statement
313-
def visitComparison_predicate(self, ctx):
314-
contains_visitor = ADQLContainsVisitor()
315-
contains_visitor.visit(ctx)
316-
if len(contains_visitor.contains):
317-
if ctx.children[0].getText().lower()[:8] == 'contains':
318-
ctx.children[1].removeLastChild()
319-
ctx.children[2].removeLastChild()
320-
elif ctx.children[2].getText().lower()[:8] == 'contains':
321-
ctx.children[0].removeLastChild()
322-
ctx.children[1].removeLastChild()
323-
324-
325302
class ADQLFunctionsTranslationVisitor(ADQLParserVisitor):
326303
"""
327304
Run this visitor after the geometry has already been processed.
@@ -371,6 +348,23 @@ def visitCentroid(self, ctx):
371348
_remove_children(ctx)
372349
self.contexts[ctx] = ctx_text
373350

351+
352+
def visitContains_predicate(self, ctx):
353+
comp_value = ctx.children[0].getText()
354+
if comp_value == '1' or comp_value == '0':
355+
self.visitContains(ctx.children[2])
356+
ctx_text = self.contexts[ctx.children[2]]
357+
if self.output_sql == 'mysql':
358+
ctx_text = f"{comp_value} = {ctx_text}"
359+
elif self.output_sql == 'postgresql' and comp_value == '0':
360+
ctx_text = ctx_text.replace('@', '!@')
361+
else:
362+
raise QueryError('The function CONTAINS allows comparison to 1 or 0 only.')
363+
364+
_remove_children(ctx)
365+
self.contexts[ctx] = ctx_text
366+
367+
374368
def visitContains(self, ctx):
375369
arg = (self.contexts[ctx.children[2].children[0]],
376370
self.contexts[ctx.children[4].children[0]])
@@ -646,9 +640,6 @@ def to_postgresql(self):
646640

647641
self.parse()
648642

649-
comp_visitor = ADQLComparisonPredicateVisitor()
650-
comp_visitor.visit(self.tree)
651-
652643
translator_visitor = ADQLGeometryTranslationVisitor(
653644
output_sql='postgresql')
654645
translator_visitor.visit(self.tree)

src/queryparser/testing/test_postgresql.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22

3-
from .utils import _test_parsing, _test_indexed_adql_translation
3+
from .utils import _test_parsing
44

55
from queryparser.postgresql import PostgreSQLQueryProcessor
66
from queryparser.exceptions import QueryError, QuerySyntaxError
@@ -35,7 +35,3 @@ def test_postrgresql_query(t):
3535
with pytest.raises(QueryError):
3636
PostgreSQLQueryProcessor(t)
3737

38-
39-
@pytest.mark.parametrize("t", tests['indexed_adql_postgres_tests'])
40-
def test_indexed_adql_postrgresql_query(t):
41-
_test_indexed_adql_translation(t + ['postgresql'])

src/queryparser/testing/tests.yaml

+12-18
Original file line numberDiff line numberDiff line change
@@ -773,24 +773,6 @@ postgresql_tests:
773773
- ['gdr2.gaia_source']
774774

775775

776-
# Each test below consists of:
777-
#
778-
# - ADQL query string
779-
# - translated query string with replaced indexed object
780-
# - dictionary of indexed objects to be replaced and their specifications
781-
782-
indexed_adql_postgres_tests:
783-
-
784-
- SELECT ra FROM gdr2.gaia_source AS gaia
785-
WHERE 1=CONTAINS(POINT('ICRS', gaia.ra, gaia.dec),
786-
CIRCLE('ICRS', 245.8962, -26.5222, 0.5));
787-
- SELECT ra FROM gdr2.gaia_source AS gaia WHERE gaia.pos @ scircle(spoint(RADIANS(245.8962), RADIANS(-26.5222)), RADIANS(0.5));
788-
- spoint:
789-
-
790-
- !!python/tuple ['gdr2', 'gaia_source', 'ra']
791-
- !!python/tuple ['gdr2', 'gaia_source', 'dec']
792-
- 'pos'
793-
794776

795777
# Each test below consists of:
796778
#
@@ -814,6 +796,10 @@ adql_mysql_tests:
814796
- SELECT TOP 10 ra, dec FROM db.tab WHERE 1=CONTAINS(POINT('ICRS', ra, dec), BOX('ICRS', -3.0, 5.0, 4.0, 10.0));
815797
- SELECT `ra`, `dec` FROM `db`.`tab` WHERE 1 = srcontainsl(spoint(RADIANS(`ra`), RADIANS(`dec`)), sbox(spoint(RADIANS(-5.000000000000),RADIANS(0.000000000000)),spoint(RADIANS(-1.000000000000),RADIANS(10.000000000000)))) LIMIT 10;
816798

799+
-
800+
- SELECT TOP 10 ra, dec FROM db.tab WHERE 0=CONTAINS(POINT('ICRS', ra, dec), BOX('ICRS', -3.0, 5.0, 4.0, 10.0));
801+
- SELECT `ra`, `dec` FROM `db`.`tab` WHERE 0 = srcontainsl(spoint(RADIANS(`ra`), RADIANS(`dec`)), sbox(spoint(RADIANS(-5.000000000000),RADIANS(0.000000000000)),spoint(RADIANS(-1.000000000000),RADIANS(10.000000000000)))) LIMIT 10;
802+
817803
-
818804
- SELECT TOP 10 DISTANCE(POINT('ICRS', ra, dec), POINT('ICRS', 13.66, -58.3)) FROM db.tab;
819805
- SELECT DEGREES(sdist(spoint(RADIANS(`ra`), RADIANS(`dec`)), spoint(RADIANS(13.66), RADIANS(-58.3)))) FROM `db`.`tab` LIMIT 10;
@@ -874,6 +860,14 @@ adql_postgresql_tests:
874860
- SELECT TOP 10 ra, dec FROM db.tab WHERE 1=CONTAINS(POINT(ra, dec), BOX(-3.0, 5.0, 4.0, 10.0));
875861
- SELECT ra, dec FROM db.tab WHERE spoint(RADIANS(ra), RADIANS(dec)) @ sbox(spoint(RADIANS(-5.000000000000),RADIANS(0.000000000000)),spoint(RADIANS(-1.000000000000),RADIANS(10.000000000000))) LIMIT 10;
876862

863+
-
864+
- SELECT TOP 10 ra, dec FROM db.tab WHERE 1=CONTAINS(POINT(ra, dec), CIRCLE(POINT(-3.0, 4.0), 10.0));
865+
- SELECT ra, dec FROM db.tab WHERE spoint(RADIANS(ra), RADIANS(dec)) @ scircle(spoint(RADIANS(-3.0), RADIANS(4.0)), RADIANS(10.0)) LIMIT 10;
866+
867+
-
868+
- SELECT TOP 10 ra, dec FROM db.tab WHERE 0=CONTAINS(POINT(ra, dec), CIRCLE(POINT(-3.0, 4.0), 10.0));
869+
- SELECT ra, dec FROM db.tab WHERE spoint(RADIANS(ra), RADIANS(dec)) !@ scircle(spoint(RADIANS(-3.0), RADIANS(4.0)), RADIANS(10.0)) LIMIT 10;
870+
877871
-
878872
- SELECT TOP 10 LOG10(ra), LOG(dec) FROM db.tab WHERE 1=CONTAINS(POINT('ICRS', ra, dec), POLYGON('ICRS', 10.0, -10.5, 20.0, 20.5, 30.0, 30.5));
879873
- SELECT LOG(ra), LN(dec) FROM db.tab WHERE spoint(RADIANS(ra), RADIANS(dec)) @ spoly('{(10.0d,-10.5d),(20.0d,20.5d),(30.0d,30.5d)}') LIMIT 10;

src/queryparser/testing/utils.py

-13
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,3 @@ def _test_parsing(query_processor, test, translate=False):
5151
if tables is not None:
5252
assert set(tables) == set(qp_tables)
5353

54-
55-
56-
def _test_indexed_adql_translation(test):
57-
query, translated_query, iob, output = test
58-
adt = ADQLQueryTranslator(query)
59-
60-
if translated_query is not None:
61-
if output == 'postgresql':
62-
tq = adt.to_postgresql()
63-
qp = PostgreSQLQueryProcessor()
64-
qp.set_query(tq)
65-
qp.process_query(indexed_objects=iob)
66-
assert translated_query.strip() == qp.query

0 commit comments

Comments
 (0)