Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions coddpiece/aggregates.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ def output_domain(self, schema: Schema) -> type:
# attr == "*" only makes sense for COUNT, but guard defensively.
if self.attr == "*":
return int
# Cross-file invariant: the only caller, Grouping._schema(), runs
# after Grouping.__post_init__ has already verified each non-"*"
# aggregate attr exists on the child schema, so this lookup cannot
# raise here in normal flow. (Calling output_domain directly on an
# AggSpec with a bogus attr would still raise AttributeError_.)
return schema[self.attr].domain

def algebra(self) -> str:
Expand Down
8 changes: 8 additions & 0 deletions coddpiece/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@
* Each expression node is visited exactly once per compile, with
one documented exception: Division, which needs the dividend
subtree twice to build its correlated subquery.
* Parameters are appended to self.params in the SAME left-to-right
order their placeholders appear in the emitted SQL text. Positional
paramstyles (qmark "?", format "%s") bind the Nth value to the Nth
placeholder, so any compile method that emits multiple sources must
visit them in textual order. Division relies on this most subtly:
the outer dividend FROM is rendered first (via _as_source at the
top), then the divisor, then the re-visited inner dividend — which
is exactly the order those placeholders appear in the final string.
"""

from __future__ import annotations
Expand Down
5 changes: 5 additions & 0 deletions coddpiece/predicates.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ def __repr__(self) -> str:
# only hash-equal if they reference the exact same relation instance. This
# is correct for the expression tree where identity, not structural equality,
# determines equivalence.
# Non-obvious mechanic: @dataclass(frozen=True) would normally synthesize
# both __eq__ and __hash__, but dataclass only fills in dunders NOT already
# defined in the class body — so these hand-written __eq__/__hash__ win and
# coexist with frozen. (The expression-tree nodes in relation.py take the
# opposite route, passing eq=False to suppress the synthesized pair.)
def __hash__(self) -> int:
return hash((id(self.source), self.name))

Expand Down
Loading