| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
The Hypothesis strategies that draw model schemas, databases, seeds and conditions move from test_expand_spec.py to expand_strategies.py, so that the planner's tests can draw from them too. No test changes.
Add datajoint.expand, the planner behind Diagram.expand (diagram.md, "Traversal specification", Computation): - TraversalSchema, a schema description built from connection.dependencies and the headings: tables, primary keys, nullable attributes, foreign keys with their attr_map, and each master's owned parts with their owning attributes (A1). - The plan: Seed, Literal, Restrict, Lift, Reexpand and Or nodes that name the restrictions they use by handle. plan_expand makes one pass over the group quotient for down and up (both is the up pass seeded with the down pass), copies a condition only under R1's rule, relabels literal keys under the Seeds rule, and marks the groups whose foreign keys link instances to iterate. - Two evaluators: RowEvaluator on rows held in memory, and QueryCompiler, which compiles each node to a FreeTable with one condition and reads rows only to find the master keys of a group that iterates. tests/unit/test_expand_plan.py (L0') checks the plan against the reference model: the description agrees with the model, the plan evaluated in memory selects D(S), U(S) and U(D(S)) for every seed kind, plan structure for the catalog shapes, and a delete simulation. The simulation leaves DB - D(S) when no lift is evaluated at delete time, and when the nodes the spec says to materialize are materialized; with the plan's own flags it is a strict xfail until delete-time materialization lands.
Diagram.expand(seed, direction) returns a diagram whose nodes are the table-level reach of the seed and whose d[T], counts() and iteration give each table's rows as a restricted query expression, compiled from the plan. It loads the schemas reachable in the direction of travel, refuses projections, joins, aggregations and unions as seeds, and replaces a dj.Top seed by its keys. cascade() and trace() are unchanged. tests/integration/test_expand_planner.py checks S1, S3, S7, G1, G4, G7 and K1, K3-K6 of the shape catalog on MySQL and PostgreSQL.
There was a problem hiding this comment.
This is a careful piece of work: the plan as plain data, two evaluators checked against the reference model, and the mutation results in the description make the harness's strength concrete. I read expand.py and the diagram.py additions. I'm leaving this as a comment for now because it sits on #1621 and datajoint/datajoint-docs#249, which are both still under review; I'd approve once those are settled.
One correctness issue in the copy rule:
A string condition's attributes come from a parser that can miss them, and an empty set allows the copy. seed_from_expression (expand.py:1275) takes frozenset(expr.restriction_attributes). For a string condition that comes from extract_column_names, which is documented as MySQL-specific: it removes everything inside "…" first and only matches lowercase identifiers. On PostgreSQL, Parent & '"parent_note" = 1' therefore yields frozenset(), which copy_allowed (:335) accepts as a subset of every foreign key's attributes. The raw string is then copied onto each unrenamed, non-nullable child, so the query fails with "column does not exist", or selects the wrong rows when the child has a secondary attribute of the same name. On MySQL, an uppercase PARENT_NOTE=1 takes the same path. Treating string conditions as attributes=None (never copied), or checking the parsed names against the seed table's heading and refusing to copy when any expected column is missing, would close it. This is close to question 1 in the description, so it may also be worth a line in the spec.
Smaller: a few source comments cite issues or versions (:169, :398, :1206 "as in 2.3.5, #1610"). Where they state an invariant, like the %-escaping one at :1110, the invariant is enough without the issue number.
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Stacked on #1621 (the reference model). This PR adds the planner behind Diagram.expand, the classmethod itself, and tests that compare the planner with the reference model. cascade(), trace() and Table.delete() are unchanged; they move onto the planner in later PRs.
The normative definitions are the "Traversal specification" section of diagram.md and the Traversal Shape Catalog in datajoint/datajoint-docs#249.
What it adds
src/datajoint/expand.py works in three steps, as the specification's "Computation" section describes them.
Diagram.expand(seed, direction="down") returns a diagram whose nodes are the seed's table-level reach. On it, d[T] returns table T restricted to its rows (by class, instance, full table name or class name), counts() counts the rows, and iteration yields each node's restricted table in the plan's topological order. The rest of the expanded-diagram contract comes later: copying, +, the operators that raise, and prune().
How it maps to the specification
Tests
tests/unit/test_expand_plan.py, with no database:
tests/integration/test_expand_planner.py, on MySQL and PostgreSQL: S1, S3, S7, G1, G4, G7 and K1, K3–K6 return the rows the catalog states in every table, in each direction the catalog lists. Refused seeds and unknown directions raise. The full catalog, the differential tests on random schemas and the delete tests follow.
Harness strength. Each of these planner mutations fails between 4 and 13 tests in test_expand_plan.py (ci profile):
Questions for the specification
For datajoint/datajoint-docs#249:
Next