sadpandajoe opened a new pull request, #45795:
URL: https://github.com/apache/superset/pull/45795

   ### SUMMARY
   Saving a SQL Lab query under a name that another saved query of the same 
user on the same database already uses succeeded silently, leaving 
indistinguishable rows in the Saved Queries list. Creating or renaming a saved 
query to a taken name is now rejected, and the Save query modal shows the 
reason next to the Name field.
   
   **Root cause**
   - `SavedQuery.label` is a plain `String(256)` with no unique constraint, 
`validators_columns` only rejects blank labels, and `pre_add`/`pre_update` only 
stripped the label, so nothing in `POST`/`PUT /api/v1/saved_query/` looked for 
an existing record with the same name.
   - On the frontend, the `saveQuery` and `updateSavedQuery` thunks caught 
every error, showed a toast and resolved, and the modal closed unconditionally 
after `await onSave(...)`/`await onUpdate(...)`. A rejected save therefore 
could not be shown in the modal and its input was lost. A failed save also fell 
through to the "Your query was saved" path in the editor.
   
   **Backend**
   - `SavedQueryDAO.validate_label_uniqueness` looks for another saved query 
with the same (stripped) label created by the same user on the same database 
(`db_id`, `IS NULL`-aware).
   - `SavedQueryRestApi.pre_add`/`pre_update` call it after the existing strip 
and raise `SavedQueryLabelDuplicateError` (HTTP 422, body `{"error": "A saved 
query with this name already exists"}`).
   - Comparison is exact and case-sensitive on the stripped label, so a padded 
copy (`" x "`) is not a way around it. Renaming a query to its own current 
name, and editing only the description or SQL of a query whose label is 
unchanged, still work; this keeps pre-existing duplicates editable. The scope 
is the creating user (`created_by_fk`), the same column the list and `PUT` 
permissions use, so rows without `user_id` (legacy and imported) are covered 
too.
   - No migration and no unique index: existing duplicate rows are left alone.
   
   **Frontend**
   - The thunks now reject so callers know the save failed. A 422 rejects with 
the server message and shows no toast; any other failure keeps the existing 
toast and rejects with the original error. Because the rejection reaches 
`onSaveQuery`, the editor no longer reports "Your query was saved" after a 
failed save.
   - The modal stays open on a rejected save, keeps the name and description, 
shows the message under the Name input (`aria-invalid` / `aria-describedby`, 
`role="alert"`), clears it when the name changes or the modal closes, and 
ignores a rejection or success that settles after the user edited the name or 
closed the modal. Save / Save as new / Update are disabled while a request is 
pending so a double click cannot submit twice.
   
   **Known limits**
   - The check is best-effort: without a database constraint, two truly 
concurrent requests can still both succeed. A constraint would need a migration 
and a cleanup of existing duplicates, which is out of scope here.
   - Saved queries created through the MCP `save_sql_query` tool or by 
importing an export bundle do not go through these API hooks and are not 
checked.
   - Creating or renaming a saved query to a taken name through the REST API 
now returns 422 instead of 201/200.
   
   ### BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
   Before: saving a second query named `Baseline Query` closes the modal with a 
success toast, and the Saved Queries list shows two rows with that name. After: 
the modal stays open with "A saved query with this name already exists" under 
the Name field and nothing is saved. Screenshots and recordings will be added 
to this section.
   
   ### TESTING INSTRUCTIONS
   1. Open SQL Lab, run any query, then Save > Save query with the name 
`Baseline Query` and save.
   2. Open a new tab on the same database, Save > Save query with the same 
name. The modal stays open and shows "A saved query with this name already 
exists" under Name; change the name and save again to succeed.
   3. Reopen the first saved query, change its name to one used by another of 
your saved queries on that database and click Update; the same message appears. 
Update with an unchanged name, or only a new description, still works.
   4. The same name on a different database, or saved by a different user, is 
accepted.
   
   Automated: `pytest 
tests/integration_tests/queries/saved_queries/api_tests.py -k duplicate_label` 
(create and rename guards, plus padded-label, other database, other user, 
own-label and legacy-duplicate cases) and `jest src/SqlLab/components/SaveQuery 
src/SqlLab/actions/sqlLab.test.ts` (rejected save/update keeps the modal open 
and shows the error, stale and overlapping requests, thunk rejection paths).
   
   ### ADDITIONAL INFORMATION
   - [ ] Has associated issue:
   - [ ] Required feature flags:
   - [x] Changes UI
   - [ ] Includes DB Migration (follow approval process in 
[SIP-59](https://github.com/apache/superset/issues/13351))
     - [ ] Migration is atomic, supports rollback & is backwards-compatible
     - [ ] Confirm DB migration upgrade and downgrade tested
     - [ ] Runtime estimates and downtime expectations provided
   - [ ] Introduces new feature or API
   - [ ] Removes existing feature or API
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to