Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adql query parser function #696

Draft
wants to merge 42 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
5f710cd
adql query parser function
burnout87 Jun 24, 2024
5526e61
queryparser-python3 lib
burnout87 Jun 25, 2024
7d099aa
more error check
burnout87 Jun 25, 2024
fb87a08
adding info
burnout87 Jul 8, 2024
a90cc05
using sqlparse library
burnout87 Jul 18, 2024
ba08496
requirements
burnout87 Jul 18, 2024
6dcc1e2
using the two libraries combined
burnout87 Jul 18, 2024
eec215c
some where clauses extraction
burnout87 Jul 23, 2024
6759c7a
testing breadth-first
burnout87 Jul 26, 2024
0e24a34
Merge branch 'master' into ivoa_helper-endpoint
burnout87 Jul 29, 2024
90c8e4f
using breadth-first
burnout87 Jul 29, 2024
2c0030b
no sqlparse
burnout87 Jul 30, 2024
16580c2
querying mysql gallery database
burnout87 Jul 30, 2024
30d1ed8
todo and removed commented lines
burnout87 Jul 31, 2024
27708b2
vo options in the dispatcher config
burnout87 Jul 31, 2024
83d119a
extracting mysql parameters from config
burnout87 Jul 31, 2024
89f46d6
no need for breadth first search
burnout87 Jul 31, 2024
b828910
sentry in case of error
burnout87 Jul 31, 2024
34bdea8
capturing general exception
burnout87 Jul 31, 2024
1ee31a1
dispatcher endpoint
burnout87 Jul 31, 2024
40dc17e
build product gallery path and jsonify the response
burnout87 Jul 31, 2024
60c9bff
var renaming
burnout87 Aug 12, 2024
bdc61b4
removed unused imports
burnout87 Aug 16, 2024
3d8d973
Merge branch 'master' into ivoa_helper-endpoint
burnout87 Aug 16, 2024
841928f
Merge branch 'master' into ivoa_helper-endpoint
burnout87 Aug 16, 2024
b31c634
Merge branch 'master' into ivoa_helper-endpoint
burnout87 Sep 25, 2024
5342a7a
using postgresql
burnout87 Sep 26, 2024
bf1cd49
sanitize request values
burnout87 Sep 26, 2024
5d53425
using value var
burnout87 Sep 27, 2024
309e2ad
postgresql connector library
burnout87 Sep 27, 2024
08ad2ab
no query parsing
burnout87 Sep 27, 2024
ec8d018
not needed import
burnout87 Sep 27, 2024
f5df283
adapted conf example
burnout87 Sep 27, 2024
f9b934b
not needed requirements
burnout87 Sep 27, 2024
87256a9
not needed requirements
burnout87 Sep 27, 2024
db388d8
freezing version pytest-xdist
burnout87 Sep 27, 2024
d8d26a7
adapted config tests and new config test
burnout87 Sep 27, 2024
c81decd
Merge branch 'master' into ivoa_helper-endpoint
burnout87 Oct 4, 2024
0dd8cb5
exception handling
burnout87 Oct 4, 2024
2c1baf4
logging
burnout87 Oct 4, 2024
73f9ede
fixing imports
burnout87 Oct 4, 2024
ebf7b24
missing fixture decorator
burnout87 Oct 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions cdci_data_analysis/analysis/ivoa_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import antlr4
Fixed Show fixed Hide fixed
from queryparser.adql import ADQLQueryTranslator
from queryparser.postgresql import PostgreSQLQueryProcessor
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
from queryparser.postgresql.PostgreSQLParser import PostgreSQLParser
from queryparser.exceptions import QuerySyntaxError
from collections import deque

from queryparser.postgresql.PostgreSQLParserListener import PostgreSQLParserListener

import sqlparse
import json

from ..app_logging import app_logging

from ..analysis import drupal_helper
Fixed Show fixed Hide fixed

logger = app_logging.getLogger('ivoa_helper')


class WhereClauseListener(PostgreSQLParserListener):
def __init__(self):
self.where_clause = None

def enterWhere_clause(self, ctx):
conditions = self.analyze_expressions(ctx)
self.where_clause = conditions

def analyze_expressions(self, node):
output_obj = dict()
for child in node.getChildren():
if isinstance(child, PostgreSQLParser.ExpressionContext):
output_obj['conditions'] = self.extract_conditions_from_hierarchy(child)
return output_obj

# def extract_conditions_from_hierarchy(self, context, level=0, conditions=None):
# bottom_reached = False
# if conditions is None:
# conditions = []
# if isinstance(context, antlr4.ParserRuleContext):
# print(f"{' ' * level} - {type(context).__name__}, level: {level}")
# if isinstance(context, PostgreSQLParser.Bool_primaryContext):
# print("Bool_primaryContext reached")
# conditions.append({})
# elif isinstance(context, PostgreSQLParser.Column_nameContext):
# print("Column_nameContext reached")
# # conditions[-1]['column'] = context.getText()
# bottom_reached = True
# elif isinstance(context, PostgreSQLParser.Relational_opContext):
# print("Relational_opContext reached")
# bottom_reached = True
# # conditions[-1]['operator'] = context.getText()
# elif isinstance(context, PostgreSQLParser.Number_literalContext):
# print("Number_literalContext reached")
# # conditions[-1]['value'] = context.getText()
# bottom_reached = True
# if not bottom_reached:
# for child in context.children:
# print(f"{' ' * level} - {type(child).__name__}, level: {level}, childGetText: {child.getText()}, conditions size: {len(conditions)}")
# conditions.extend(self.extract_conditions_from_hierarchy(child, level + 1, conditions=conditions))
# return conditions
Fixed Show fixed Hide fixed

from collections import deque

def extract_conditions_from_hierarchy(self, context, conditions=None):
if conditions is None:
conditions = []

queue = deque([(context, 0)])

while queue:
context, level = queue.popleft()

if isinstance(context, antlr4.ParserRuleContext):
print(f"{' ' * level} - {type(context).__name__}, level: {level}")
if isinstance(context, PostgreSQLParser.Bool_primaryContext):
print("Bool_primaryContext reached")
conditions.append({})
elif isinstance(context, PostgreSQLParser.Column_nameContext):
print("Column_nameContext reached")
conditions[-1]['column'] = context.getText()
elif isinstance(context, PostgreSQLParser.Relational_opContext):
print("Relational_opContext reached")
conditions[-1]['operator'] = context.getText()
elif isinstance(context, PostgreSQLParser.Number_literalContext):
print("Number_literalContext reached")
conditions[-1]['value'] = context.getText()
# else:
# Enqueue all children of the current node, with their level increased by 1
for child in context.children:
print(
f"{' ' * level} - {type(child).__name__}, level: {level}, childGetText: {child.getText()}, conditions size: {len(conditions)}")
queue.append((child, level + 1))

return conditions


def parse_adql_query(query):
try:
# queryparser
adt = ADQLQueryTranslator(query)
qp = PostgreSQLQueryProcessor()
where_listener = WhereClauseListener()
qp.set_query(adt.to_postgresql())
qp.process_query()

inpt = antlr4.InputStream(query)
lexer = qp.lexer(inpt)
stream = antlr4.CommonTokenStream(lexer)
parser = qp.parser(stream)
tree = parser.query()
qp.walker.walk(where_listener, tree)

output_obj = dict(
columns=qp.display_columns,
tables=qp.tables,
rest=qp,
where_clause=where_listener.where_clause
)

# sqlparse
parsed_query_obj = sqlparse.parse(query)[0]

for t in parsed_query_obj.tokens:
if isinstance(t, sqlparse.sql.Where):
output_obj['where_token'] = t

except QuerySyntaxError as qe:
logger.error(f'Error parsing ADQL query: {qe}')
output_obj = dict(
where_clause=None,
tables=None,
columns=None,
rest=None
)
return output_obj


def run_ivoa_query(query, sentry_dsn=None, **kwargs):
result_list = []
parsed_query_obj = parse_adql_query(query)

tables = parsed_query_obj.get('tables', [])
if len(tables) == 1 and tables[0] == 'product_gallery':
logger.info('Query is a product_gallery query')
product_gallery_url = kwargs.get('product_gallery_url', None)
gallery_jwt_token = kwargs.get('gallery_jwt_token', None)
if product_gallery_url and gallery_jwt_token:
result_list = run_ivoa_query_from_product_gallery(
product_gallery_url,
gallery_jwt_token,
sentry_dsn=sentry_dsn,
**kwargs
)
return result_list


def run_ivoa_query_from_product_gallery(product_gallery_url,
gallery_jwt_token,
sentry_dsn=None,
**kwargs):
output_get = drupal_helper.get_data_product_list_by_source_name_with_conditions(
product_gallery_url=product_gallery_url,
gallery_jwt_token=gallery_jwt_token,
sentry_dsn=sentry_dsn,
**kwargs)

output_list = json.dumps(output_get)

return output_list
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ GitPython
nbformat
sentry-sdk
pytest-sentry
sqlparse
queryparser-python3
-e git+https://github.com/oda-hub/oda_api.git#egg=oda_api

MarkupSafe==2.0.1
Expand Down
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
"black>=22.10.0",
"bs4",
"GitPython",
"sqlparse",
"queryparser-python3",
"nbformat",
"giturlparse",
"sentry-sdk",
Expand Down
Loading