diff --git a/Docs/KnownIssues.md b/Docs/KnownIssues.md index a0a9854..34c10c2 100644 --- a/Docs/KnownIssues.md +++ b/Docs/KnownIssues.md @@ -1520,6 +1520,100 @@ that does not exist. --- +## Found by clicking through Studio before 3.1.1, 2026-08-19 + +Both of the engine entries below were reproduced against a bare `WitSqlEngine` over an in-memory +database, with no Studio in the picture. The third is a gap in Studio itself, recorded here +because a person meeting it will look for it here. + +--- + +## 25. A join condition written the other way round is refused + +> Measured 2026-08-19 on engine 14.0.0. **Root cause identified**, fix not written. + +In `A JOIN B ON x = y`, the column of the LEFT input has to be written FIRST. Written the other +way, the query fails at execution with a `KeyNotFoundException`: + +```sql +SELECT c.Country, o.Total FROM Customers c JOIN Orders o ON c.Id = o.CustomerId -- 3 rows +SELECT c.Country, o.Total FROM Customers c JOIN Orders o ON o.CustomerId = c.Id -- Column 'CustomerId' not found +SELECT Customers.Country FROM Customers JOIN Orders + ON Orders.CustomerId = Customers.Id -- the same, without aliases +``` + +**In a chain of joins, "the left input" is everything joined so far**, which is the shape most +likely to be met by accident: + +```sql +SELECT c.Country FROM Customers c JOIN Orders o ON c.Id = o.CustomerId + JOIN Items i ON i.OrderId = o.Id -- Column 'OrderId' not found +``` + +**Root cause.** `Optimizers/OptimizerJoinCondition.TryExtractEquiJoinKey` builds the key pair as +`LeftKey = binary.Left, RightKey = binary.Right` - it takes **the written order of the equality** +for the order of the join inputs. It checks that the two column references carry different table +qualifiers and never checks WHICH input each belongs to, so `IteratorHashJoin.ComputeHashKey` +evaluates the right table’s column against rows of the left one and the evaluator throws. + +**Where a fix belongs:** `Query/QueryPlanner.Sources.cs`, `CreateJoinIterator` - it already holds +both input iterators and therefore both schemas, so each pair can be oriented before the iterator +is built. Unqualified columns are already sent to the residual condition, so only the qualified +case needs it. + +**What was measured, and what it means for a workaround:** + +| written as | result | +|---|---| +| `INNER JOIN ... ON left.x = right.y` | works | +| `INNER JOIN ... ON right.y = left.x` | **fails** | +| `LEFT JOIN ... ON right.y = left.x` | works | +| `FROM a, b WHERE right.y = left.x` | works | + +So: **write the left side’s column first**, or put the condition in `WHERE` with a comma join. +The failure is in the hash-join path, and the planner chose a hash join even for two-row tables. + +**Why the suite never saw it:** every JOIN case in the engine writes the equality in the same +order. + +--- + +## 26. `EXPLAIN` gives the right input’s child the wrong parent + +> Measured 2026-08-19 on engine 14.0.0. + +For `SELECT c.Country, o.Total FROM Customers c JOIN Orders o ON c.Id = o.CustomerId LIMIT 3`, +`EXPLAIN` answers: + +``` +id parent detail +0 -1 LIMIT +1 0 PROJECT +2 1 HASH INNER JOIN +3 2 ALIAS c +4 3 SCAN TABLE Customers +5 2 ALIAS o +6 3 SCAN TABLE Orders <- parent should be 5 +``` + +`SCAN TABLE Orders` is reported as a child of `ALIAS c`. Anything that draws the plan as a tree - +Studio’s Plan panel does - draws the wrong tree, faithfully. **The renderer is not the defect.** + +--- + +## 27. Studio: a function or a procedure can only be refreshed + +> Studio 3.1.1. A gap rather than a wrong answer. + +The tree’s context menu offers a routine exactly one item, `Refresh`. The engine has +`DROP FUNCTION` and `DROP PROCEDURE`, and the catalogue already carries the routine’s body - the +inspector on the right shows it - so both *View definition* and *Drop* are possible and neither is +offered. Every other kind of object in the tree was given its own menu in 3.1.0; routines were not +included. + +Until they are: drop a routine by running the statement in a query tab. + +--- ## Verifying a fix WitAnalytics is a ready-made regression harness: its stats test fixture runs the diff --git a/Tools/OutWit.Database.Studio/CHANGELOG.md b/Tools/OutWit.Database.Studio/CHANGELOG.md index e5d9a08..d3f0c6d 100644 --- a/Tools/OutWit.Database.Studio/CHANGELOG.md +++ b/Tools/OutWit.Database.Studio/CHANGELOG.md @@ -3,6 +3,48 @@ Studio is versioned separately from the WitDatabase engine and released under its own `studio-v*` tag. The engine's changelog is `/CHANGELOG.md`. +## 3.1.1 + +**Four corrections, three of them to things 3.1.0 itself broke.** They were found the day 3.1.0 +shipped, by using it. + +Engine: 14.0.0, unchanged. + +### The double click works again + +A double click on a table opens its data. It had done nothing at all since the repair that was +meant to fix it: `DoubleTapped` travels `Bubble` alone, `AddHandler` takes the route as a plain +argument and cannot refuse an impossible one, so the handler was registered on a route the event +does not travel and was never called - through a green suite, a green CI and a signed release. The +double click is now read from the pointer, which does tunnel. + +The row no longer opens as well: handling the press does not stop the tap, so the tree still +toggled itself, and a table both opened its data and opened its row. + +**And a double click on the connection now opens the tab that describes the database** - the same +one *Database…* opens in the menu. One rule rather than three exceptions: a double click opens the +thing the node IS. Which node opens what moved out of the code-behind, where no test could read +it. + +### The menu draws no rule with nothing beside it + +Giving each node its own menu taught every ITEM to hide itself where it does not apply, and left +the five separators drawing the shape of a menu that is no longer there: a connection got two +rules with nothing between them and two more below its last item, a folder began with a rule, a +column was one command wrapped in five. All fourteen kinds of node were wrong, the table included. + +### The status line takes back what is no longer true + +With no editor open anywhere the line still read *Editing table: Products*. What HAPPENED stays - +«executed in 9 ms» is true afterwards - but what IS ends with the tab it belongs to. + +### Known issues + +Two engine defects were found in the same pass and are **not** fixed here, because the engine did +not move: a join condition written `ON right.x = left.y` is refused, and `EXPLAIN` gives the right +input’s child the wrong parent. Both are written up with their root cause in +[Docs/KnownIssues.md](../../Docs/KnownIssues.md) as issues 25 and 26; issue 27 is a gap in Studio +itself - a routine in the tree can only be refreshed. ## 3.1.0 **Forty-six findings, and what looking for them turned up.** Taking the screenshots for the diff --git a/Tools/OutWit.Database.Studio/OutWit.Database.Studio.csproj b/Tools/OutWit.Database.Studio/OutWit.Database.Studio.csproj index b4a5ade..03b2450 100644 --- a/Tools/OutWit.Database.Studio/OutWit.Database.Studio.csproj +++ b/Tools/OutWit.Database.Studio/OutWit.Database.Studio.csproj @@ -18,7 +18,7 @@ - 3.1.0 + 3.1.1 WitDatabase Studio OutWit