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
94 changes: 94 additions & 0 deletions Docs/KnownIssues.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions Tools/OutWit.Database.Studio/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Tools/OutWit.Database.Studio/OutWit.Database.Studio.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

<!-- Release info. The release workflow overrides Version from the studio-v* tag; this
is what a local build gets. -->
<Version>3.1.0</Version>
<Version>3.1.1</Version>
<Product>WitDatabase Studio</Product>
<Company>OutWit</Company>
</PropertyGroup>
Expand Down
Loading