bitemyapp / bitemyapp/esqueleto

`except_` drops explicit right-associated grouping

Open
#437 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Haskell
Stars
399
Forks
107
Avg merge
1d 17h
Merged PRs (30d)
1

Description

## Description

`SqlSetOperation` loses explicit right-associated grouping for `except_`.
Because SQL `EXCEPT` is not associative, this changes query results.

The Haskell functions currently have no fixity declarations, so an
unparenthesized chain defaults to left-associative parsing. More importantly,
even when the caller explicitly constructs a right-associated expression, the
renderer flattens the set-operation tree and omits the required SQL
parentheses.

## Behavioral reproduction

```haskell
itDb "preserves right-associated EXCEPT nesting" $ do
let singleton :: Int -> SqlQuery (SqlExpr (Value Int))
singleton n = pure $ val n
(result :: [Value Int]) <- select $ Experimental.from $
singleton 1 `except_` (singleton 1 `except_` singleton 1)
asserting $ result `shouldBe` [Value 1]
```

Expected result:

```haskell
[Value 1]
```

Actual result:

```haskell
[]
```

Semantically, the inner `1 EXCEPT 1` is empty, so the outer operation should
return `1`. The generated SQL is effectively evaluated as the left-associated
expression instead:

```sql
(SELECT 1 EXCEPT SELECT 1) EXCEPT SELECT 1
```

rather than:

```sql
SELECT 1 EXCEPT (SELECT 1 EXCEPT SELECT 1)
```

## Likely cause

`mkSetOperation` recursively renders both operands with the enclosing
`NeedParens` value and concatenates their clauses. A nested set operation does
not retain its position in the Haskell expression tree, so explicit grouping
is lost.

The fix should preserve nested set-operation grouping, particularly on the
right side of `except_` and in mixed-operator trees. It may also be worth
declaring/documenting the Haskell fixity of `except_` as non-associative so
chained uses require explicit grouping.

Found while reviewing #436; this issue is separate from that PR's identifier
allocation change and is reproducible on current `master`.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start at mkSetOperation and the except_ entry point, then run the provided itDb reproduction on current master. Trace how nested operands are rendered and add or update coverage for right-associated EXCEPT and mixed-operator trees; done means explicit grouping is retained and the reproduction returns [Value 1].

Written by the indexing model from the issue text.

Assessment

Tech stack
haskell, sql
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
64/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.