Skip to content

Commit

Permalink
WIP: DirectQueryDriver.
Browse files Browse the repository at this point in the history
  • Loading branch information
TallJimbo committed Feb 26, 2024
1 parent 97d3e88 commit 9b11866
Show file tree
Hide file tree
Showing 14 changed files with 2,162 additions and 9 deletions.
29 changes: 29 additions & 0 deletions python/lsst/daf/butler/direct_query_driver/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# This file is part of daf_butler.
#
# Developed for the LSST Data Management System.
# This product includes software developed by the LSST Project
# (http://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This software is dual licensed under the GNU General Public License and also
# under a 3-clause BSD license. Recipients may choose which of these licenses
# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
# respectively. If you choose the GPL option then the following text applies
# (but note that there is still no warranty even if you opt for BSD instead):
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from ._postprocessing import Postprocessing
from ._sql_builder import SqlBuilder
170 changes: 170 additions & 0 deletions python/lsst/daf/butler/direct_query_driver/_analyzed_query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# This file is part of daf_butler.
#
# Developed for the LSST Data Management System.
# This product includes software developed by the LSST Project
# (http://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This software is dual licensed under the GNU General Public License and also
# under a 3-clause BSD license. Recipients may choose which of these licenses
# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
# respectively. If you choose the GPL option then the following text applies
# (but note that there is still no warranty even if you opt for BSD instead):
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from __future__ import annotations

Check warning on line 28 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L28

Added line #L28 was not covered by tests

__all__ = ("AnalyzedQuery", "AnalyzedDatasetSearch", "DataIdExtractionVisitor")

Check warning on line 30 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L30

Added line #L30 was not covered by tests

import dataclasses
from collections.abc import Iterator
from typing import Any

Check warning on line 34 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L32-L34

Added lines #L32 - L34 were not covered by tests

from ..dimensions import DataIdValue, DimensionElement, DimensionGroup, DimensionUniverse
from ..queries import tree as qt
from ..queries.visitors import ColumnExpressionVisitor, PredicateVisitFlags, SimplePredicateVisitor
from ..registry.interfaces import CollectionRecord
from ._postprocessing import Postprocessing

Check warning on line 40 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L36-L40

Added lines #L36 - L40 were not covered by tests


@dataclasses.dataclass
class AnalyzedDatasetSearch:
name: str
shrunk: str
dimensions: DimensionGroup
collection_records: list[CollectionRecord] = dataclasses.field(default_factory=list)
messages: list[str] = dataclasses.field(default_factory=list)
is_calibration_search: bool = False

Check warning on line 50 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L44-L50

Added lines #L44 - L50 were not covered by tests


@dataclasses.dataclass
class AnalyzedQuery:
predicate: qt.Predicate
postprocessing: Postprocessing
base_columns: qt.ColumnSet
projection_columns: qt.ColumnSet
final_columns: qt.ColumnSet
find_first_dataset: str | None
materializations: dict[qt.MaterializationKey, DimensionGroup] = dataclasses.field(default_factory=dict)
datasets: dict[str, AnalyzedDatasetSearch] = dataclasses.field(default_factory=dict)
messages: list[str] = dataclasses.field(default_factory=list)
constraint_data_id: dict[str, DataIdValue] = dataclasses.field(default_factory=dict)
data_coordinate_uploads: dict[qt.DataCoordinateUploadKey, DimensionGroup] = dataclasses.field(

Check warning on line 65 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L54-L65

Added lines #L54 - L65 were not covered by tests
default_factory=dict
)
needs_dimension_distinct: bool = False
needs_find_first_resolution: bool = False
projection_region_aggregates: list[DimensionElement] = dataclasses.field(default_factory=list)

Check warning on line 70 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L68-L70

Added lines #L68 - L70 were not covered by tests

@property

Check warning on line 72 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L72

Added line #L72 was not covered by tests
def universe(self) -> DimensionUniverse:
return self.base_columns.dimensions.universe

Check warning on line 74 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L74

Added line #L74 was not covered by tests

@property

Check warning on line 76 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L76

Added line #L76 was not covered by tests
def needs_projection(self) -> bool:
return self.needs_dimension_distinct or self.postprocessing.check_validity_match_count

Check warning on line 78 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L78

Added line #L78 was not covered by tests

def iter_mandatory_base_elements(self) -> Iterator[DimensionElement]:

Check warning on line 80 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L80

Added line #L80 was not covered by tests
for element_name in self.base_columns.dimensions.elements:
element = self.universe[element_name]

Check warning on line 82 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L82

Added line #L82 was not covered by tests
if self.base_columns.dimension_fields[element_name]:
# We need to get dimension record fields for this element, and
# its table is the only place to get those.
yield element

Check warning on line 86 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L86

Added line #L86 was not covered by tests
elif element.defines_relationships:
# We als need to join in DimensionElements tables that define
# one-to-many and many-to-many relationships, but data
# coordinate uploads, materializations, and datasets can also
# provide these relationships. Data coordinate uploads and
# dataset tables only have required dimensions, and can hence
# only provide relationships involving those.
if any(
element.minimal_group.names <= upload_dimensions.required
for upload_dimensions in self.data_coordinate_uploads.values()
):
continue

Check warning on line 98 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L98

Added line #L98 was not covered by tests
if any(
element.minimal_group.names <= dataset_spec.dimensions.required
for dataset_spec in self.datasets.values()
):
continue

Check warning on line 103 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L103

Added line #L103 was not covered by tests
# Materializations have all key columns for their dimensions.
if any(
element in materialization_dimensions.names
for materialization_dimensions in self.materializations.values()
):
continue
yield element

Check warning on line 110 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L109-L110

Added lines #L109 - L110 were not covered by tests


class DataIdExtractionVisitor(

Check warning on line 113 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L113

Added line #L113 was not covered by tests
SimplePredicateVisitor,
ColumnExpressionVisitor[tuple[str, None] | tuple[None, Any] | tuple[None, None]],
):
def __init__(self, data_id: dict[str, DataIdValue], messages: list[str]):
self.data_id = data_id
self.messages = messages

Check warning on line 119 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L117-L119

Added lines #L117 - L119 were not covered by tests

def visit_comparison(

Check warning on line 121 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L121

Added line #L121 was not covered by tests
self,
a: qt.ColumnExpression,
operator: qt.ComparisonOperator,
b: qt.ColumnExpression,
flags: PredicateVisitFlags,
) -> None:
if flags & PredicateVisitFlags.HAS_OR_SIBLINGS:
return None

Check warning on line 129 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L129

Added line #L129 was not covered by tests
if flags & PredicateVisitFlags.INVERTED:
if operator == "!=":
operator = "=="

Check warning on line 132 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L132

Added line #L132 was not covered by tests
else:
return None

Check warning on line 134 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L134

Added line #L134 was not covered by tests
if operator != "==":
return None
k_a, v_a = a.visit(self)
k_b, v_b = b.visit(self)

Check warning on line 138 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L136-L138

Added lines #L136 - L138 were not covered by tests
if k_a is not None and v_b is not None:
key = k_a
value = v_b

Check warning on line 141 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L140-L141

Added lines #L140 - L141 were not covered by tests
elif k_b is not None and v_a is not None:
key = k_b
value = v_a

Check warning on line 144 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L143-L144

Added lines #L143 - L144 were not covered by tests
else:
return None

Check warning on line 146 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L146

Added line #L146 was not covered by tests
if (old := self.data_id.setdefault(key, value)) != value:
self.messages.append(f"'where' expression requires both {key}={value!r} and {key}={old!r}.")
return None

Check warning on line 149 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L148-L149

Added lines #L148 - L149 were not covered by tests

def visit_binary_expression(self, expression: qt.BinaryExpression) -> tuple[None, None]:
return None, None

Check warning on line 152 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L151-L152

Added lines #L151 - L152 were not covered by tests

def visit_unary_expression(self, expression: qt.UnaryExpression) -> tuple[None, None]:
return None, None

Check warning on line 155 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L154-L155

Added lines #L154 - L155 were not covered by tests

def visit_literal(self, expression: qt.ColumnLiteral) -> tuple[None, Any]:
return None, expression.get_literal_value()

Check warning on line 158 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L157-L158

Added lines #L157 - L158 were not covered by tests

def visit_dimension_key_reference(self, expression: qt.DimensionKeyReference) -> tuple[str, None]:
return expression.dimension.name, None

Check warning on line 161 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L160-L161

Added lines #L160 - L161 were not covered by tests

def visit_dimension_field_reference(self, expression: qt.DimensionFieldReference) -> tuple[None, None]:
return None, None

Check warning on line 164 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L163-L164

Added lines #L163 - L164 were not covered by tests

def visit_dataset_field_reference(self, expression: qt.DatasetFieldReference) -> tuple[None, None]:
return None, None

Check warning on line 167 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L166-L167

Added lines #L166 - L167 were not covered by tests

def visit_reversed(self, expression: qt.Reversed) -> tuple[None, None]:

Check warning on line 169 in python/lsst/daf/butler/direct_query_driver/_analyzed_query.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_analyzed_query.py#L169

Added line #L169 was not covered by tests
raise AssertionError("No Reversed expressions in predicates.")
61 changes: 61 additions & 0 deletions python/lsst/daf/butler/direct_query_driver/_convert_results.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# This file is part of daf_butler.
#
# Developed for the LSST Data Management System.
# This product includes software developed by the LSST Project
# (http://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This software is dual licensed under the GNU General Public License and also
# under a 3-clause BSD license. Recipients may choose which of these licenses
# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
# respectively. If you choose the GPL option then the following text applies
# (but note that there is still no warranty even if you opt for BSD instead):
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from __future__ import annotations

Check warning on line 28 in python/lsst/daf/butler/direct_query_driver/_convert_results.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_convert_results.py#L28

Added line #L28 was not covered by tests

__all__ = ("convert_dimension_record_results",)

Check warning on line 30 in python/lsst/daf/butler/direct_query_driver/_convert_results.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_convert_results.py#L30

Added line #L30 was not covered by tests

from collections.abc import Iterable
from typing import TYPE_CHECKING

Check warning on line 33 in python/lsst/daf/butler/direct_query_driver/_convert_results.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_convert_results.py#L32-L33

Added lines #L32 - L33 were not covered by tests

import sqlalchemy

Check warning on line 35 in python/lsst/daf/butler/direct_query_driver/_convert_results.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_convert_results.py#L35

Added line #L35 was not covered by tests

from ..dimensions import DimensionRecordSet

Check warning on line 37 in python/lsst/daf/butler/direct_query_driver/_convert_results.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_convert_results.py#L37

Added line #L37 was not covered by tests

if TYPE_CHECKING:
from ..queries.driver import DimensionRecordResultPage, PageKey
from ..queries.result_specs import DimensionRecordResultSpec
from ..registry.nameShrinker import NameShrinker


def convert_dimension_record_results(

Check warning on line 45 in python/lsst/daf/butler/direct_query_driver/_convert_results.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_convert_results.py#L45

Added line #L45 was not covered by tests
raw_rows: Iterable[sqlalchemy.Row],
spec: DimensionRecordResultSpec,
next_key: PageKey | None,
name_shrinker: NameShrinker,
) -> DimensionRecordResultPage:
record_set = DimensionRecordSet(spec.element)
columns = spec.get_result_columns()

Check warning on line 52 in python/lsst/daf/butler/direct_query_driver/_convert_results.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_convert_results.py#L51-L52

Added lines #L51 - L52 were not covered by tests
column_mapping = [
(field, name_shrinker.shrink(columns.get_qualified_name(spec.element.name, field)))
for field in spec.element.schema.names
]
record_cls = spec.element.RecordClass

Check warning on line 57 in python/lsst/daf/butler/direct_query_driver/_convert_results.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_convert_results.py#L57

Added line #L57 was not covered by tests
if not spec.element.temporal:
for raw_row in raw_rows:
record_set.add(record_cls(**{k: raw_row._mapping[v] for k, v in column_mapping}))
return DimensionRecordResultPage(spec=spec, next_key=next_key, rows=record_set)

Check warning on line 61 in python/lsst/daf/butler/direct_query_driver/_convert_results.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/direct_query_driver/_convert_results.py#L61

Added line #L61 was not covered by tests
Loading

0 comments on commit 9b11866

Please sign in to comment.