Skip to content

fix(file-service): reject invalid or duplicate dataset renames#6426

Merged
xuang7 merged 12 commits into
apache:mainfrom
kunwp1:fix/dataset-rename-validation
Jul 17, 2026
Merged

fix(file-service): reject invalid or duplicate dataset renames#6426
xuang7 merged 12 commits into
apache:mainfrom
kunwp1:fix/dataset-rename-validation

Conversation

@kunwp1

@kunwp1 kunwp1 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

The dataset rename endpoint previously skipped both checks that dataset creation enforces: name validation and the per owner duplicate name check. A rename could therefore produce two datasets with the same (owner, name) pair, which breaks every lookup that resolves a dataset by (owner email, dataset name). File uploads and workflow file reads fail with a TooManyRowsException for both datasets.

This PR adds the missing guards to the backend rename endpoint, and a UNIQUE (owner_uid, name) constraint at the database level so that concurrent writes racing past the application-level check cannot reintroduce duplicates. The migration script sql/updates/28.sql deduplicates pre-existing duplicates before adding the constraint (the oldest dataset keeps its name, later ones get a -<did> suffix). The frontend rename input now applies the same name validation client-side and shows the detailed reason from the backend when a rename is rejected, instead of the generic "Update dataset name failed".

Some additional changes:

  • validateDatasetName now also enforces the 128-character column limit
  • DEFAULT_DATASET_NAME changed from "Untitled dataset" to "Untitled-dataset", since the fallback name used for empty rename input must itself pass the new validation

Any related issues, documentation, discussions?

Closes #6424

How was this PR tested?

Screen.Recording.2026-07-14.at.8.23.22.PM.mov

Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Code, Claude Fable 5

updateDatasetName now applies the same name validation and per-owner
duplicate check as createDataset, backed by a new UNIQUE (owner_uid, name)
constraint on the dataset table (migration dedupes pre-existing rows).
The frontend rename input mirrors the validation and surfaces backend
error messages.
@github-actions github-actions Bot added ddl-change Changes to the TexeraDB DDL fix frontend Changes related to the frontend GUI platform Non-amber Scala service paths labels Jul 14, 2026
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Contributors with relevant context: @Ma77Ball, @carloea2, @VuMartin
    You can notify them by mentioning @Ma77Ball, @carloea2, @VuMartin in a comment.

@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

🟢 5 better · 🔴 7 worse · ⚪ 3 noise (<±5%) · 0 without baseline

Compared against main e793be5 benchmarked on this same runner, so the delta is largely free of cross-runner hardware noise. The "7d avg" column still reflects the gh-pages dashboard. Treat <±5% as noise unless repeated.

Dashboard · Run

config throughput MB/s latency max Δ latest / 7d
🔴 bs=10 sw=10 sl=64 452 0.276 21,034/32,041/32,041 us 🔴 +14.7% / 🔴 +128.3%
🔴 bs=100 sw=10 sl=64 955 0.583 103,880/130,679/130,679 us 🔴 +12.7% / 🔴 +33.6%
🟢 bs=1000 sw=10 sl=64 1,126 0.687 888,508/930,757/930,757 us 🟢 -7.5% / ⚪ within ±5%
Baseline details

Latest main e793be5 from same runner

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 452 tuples/sec 492 tuples/sec 845.85 tuples/sec -8.1% -46.6%
bs=10 sw=10 sl=64 MB/s 0.276 MB/s 0.301 MB/s 0.516 MB/s -8.3% -46.5%
bs=10 sw=10 sl=64 p50 21,034 us 19,848 us 11,663 us +6.0% +80.3%
bs=10 sw=10 sl=64 p95 32,041 us 27,930 us 14,037 us +14.7% +128.3%
bs=10 sw=10 sl=64 p99 32,041 us 27,930 us 18,666 us +14.7% +71.7%
bs=100 sw=10 sl=64 throughput 955 tuples/sec 983 tuples/sec 1,102 tuples/sec -2.8% -13.4%
bs=100 sw=10 sl=64 MB/s 0.583 MB/s 0.6 MB/s 0.673 MB/s -2.8% -13.3%
bs=100 sw=10 sl=64 p50 103,880 us 99,856 us 91,441 us +4.0% +13.6%
bs=100 sw=10 sl=64 p95 130,679 us 115,903 us 97,817 us +12.7% +33.6%
bs=100 sw=10 sl=64 p99 130,679 us 115,903 us 108,384 us +12.7% +20.6%
bs=1000 sw=10 sl=64 throughput 1,126 tuples/sec 1,064 tuples/sec 1,128 tuples/sec +5.8% -0.2%
bs=1000 sw=10 sl=64 MB/s 0.687 MB/s 0.649 MB/s 0.688 MB/s +5.9% -0.2%
bs=1000 sw=10 sl=64 p50 888,508 us 942,066 us 901,939 us -5.7% -1.5%
bs=1000 sw=10 sl=64 p95 930,757 us 1,006,395 us 940,934 us -7.5% -1.1%
bs=1000 sw=10 sl=64 p99 930,757 us 1,006,395 us 977,688 us -7.5% -4.8%
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,442.23,200,128000,452,0.276,21034.25,32041.27,32041.27
1,100,10,64,20,2093.80,2000,1280000,955,0.583,103879.90,130678.84,130678.84
2,1000,10,64,20,17766.81,20000,12800000,1126,0.687,888508.04,930757.09,930757.09

kunwp1 added 2 commits July 14, 2026 20:00
- Add length validation (max 128 chars) to validateDatasetName and fix
  its error message to accurately reflect the regex rules
- Catch UNIQUE constraint violations (SQLState 23505) in both rename and
  create paths so concurrent races return 400 instead of 500
- Use ctx.fetchExists() instead of materializing full rows for duplicate
  checks, and use dataset.getDid instead of client-supplied modificator.did
- Add client-side validation and error surfacing to the live rename UIs
  (card-item and list-item components) which are the actual rendered
  components users interact with
- Use shared extractErrorMessage utility instead of inline error parsing
- Fix DEFAULT_DATASET_NAME from "Untitled dataset" (contains space) to
  "Untitled-dataset" to comply with the new validation rules
Backend:
- validateDatasetName now throws BadRequestException directly,
  eliminating the try/catch wrappers at both call sites
- Extract failOnDuplicateDatasetName[T] helper that catches SQLState
  23505 and maps to BadRequestException, replacing two identical
  6-line catch blocks
- Use jOOQ's built-in e.sqlState() instead of the manual
  Option(e.getCause).collect dance
- Hoist regex to a class-level val (compiled once, not per call)
- Merge redundant null/empty check into the regex check (regex +
  quantifier already rejects empty strings)

Frontend:
- Export validateDatasetName() from dataset.service.ts as the single
  source of truth for the name rule (regex + max length + error message)
- All three components now call the shared validator instead of
  inlining the regex, magic number 128, and error string
@codecov-commenter

codecov-commenter commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.82609% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 70.30%. Comparing base (e793be5) to head (2a5ab99).
⚠️ Report is 18 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ache/texera/service/resource/DatasetResource.scala 91.66% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #6426      +/-   ##
============================================
+ Coverage     69.83%   70.30%   +0.46%     
- Complexity     3383     3402      +19     
============================================
  Files          1142     1142              
  Lines         44844    44876      +32     
  Branches       4949     4955       +6     
============================================
+ Hits          31318    31551     +233     
+ Misses        11874    11679     -195     
+ Partials       1652     1646       -6     
Flag Coverage Δ *Carryforward flag
access-control-service 70.00% <ø> (ø)
agent-service 76.76% <ø> (ø) Carriedforward from 81bac03
amber 66.65% <ø> (-0.03%) ⬇️
computing-unit-managing-service 17.72% <ø> (+7.75%) ⬆️
config-service 52.30% <ø> (ø)
file-service 66.80% <91.66%> (+2.82%) ⬆️
frontend 69.06% <100.00%> (+0.90%) ⬆️
notebook-migration-service 78.94% <ø> (ø)
pyamber 91.19% <ø> (ø) Carriedforward from 81bac03
workflow-compiling-service 55.14% <ø> (ø)

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

kunwp1 added 7 commits July 14, 2026 20:27
When a user enters an invalid dataset name, the input now reverts to
the original name and exits editing mode, consistent with the behavior
on backend rejection.
- On invalid dataset name: show error, revert to original name, exit
  editing mode (consistent with backend rejection behavior)
- Fix import line lengths to comply with prettier's 120-char limit
- Add unit tests for the shared validateDatasetName() function
- Add dataset rename validation tests to card-item and list-item specs
  to cover all new code paths for 100% coverage on changed lines
The error handler now uses extractErrorMessage which extracts the
message property from Error instances ("boom") rather than showing the
old hardcoded "Update dataset name failed" string.
Add null and a 129-character string to the invalid-name test cases,
covering both branches of validateDatasetName (null check and length
check). The failOnDuplicateDatasetName catch block is a race-condition
safety net that requires concurrent transactions to trigger and cannot
be deterministically exercised in a single-threaded test.
… paths

- Widen failOnDuplicateDatasetName to private[resource] so the spec can
  exercise it directly, since the race it guards cannot be triggered
  deterministically through the endpoint in a single-threaded test
- Test all three paths of failOnDuplicateDatasetName: unique violation
  mapped to BadRequestException, non-23505 DataAccessException rethrown,
  and pass-through of the result on success
- Cover the dataset rename error branch in card-item and list-item specs
  (error message surfaced, name reverted, editing mode exited)
…setName

The catch clause's type test had a partial branch: exceptions that are
not DataAccessException were never thrown through the helper. Throw an
IllegalStateException to cover the fall-through path.
scoverage instruments a guarded catch pattern (case e: X if cond) as a
multi-way branch with a synthetic arm that no test can reach, leaving a
permanent partial on that line. Restructure to an unguarded catch with a
plain if inside, whose branches are all deterministically exercised by
the existing failOnDuplicateDatasetName tests. Also add a test for a
DataAccessException whose cause carries no SQL state.
@kunwp1
kunwp1 marked this pull request as ready for review July 15, 2026 02:51
@chenlica
chenlica requested a review from xuang7 July 15, 2026 03:13
@mengw15
mengw15 requested a review from Copilot July 15, 2026 05:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a correctness gap in Texera’s dataset rename flow by enforcing the same name validation and per-owner uniqueness rules as dataset creation, and by adding a database-level uniqueness constraint to prevent concurrent races from reintroducing duplicates.

Changes:

  • Enforce dataset-name validation + per-owner duplicate checks in the file-service rename endpoint, and translate DB unique-constraint violations into a user-facing 400 error.
  • Add a DB migration that deduplicates existing (owner_uid, name) collisions and then adds UNIQUE (owner_uid, name); reflect the constraint in the baseline DDL.
  • Update the Angular dashboard to validate dataset names client-side and surface backend error messages; add/extend unit tests accordingly.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.

Show a summary per file
File Description
sql/updates/28.sql Deduplicates existing duplicate dataset names per owner and adds the (owner_uid, name) unique constraint.
sql/texera_ddl.sql Adds UNIQUE (owner_uid, name) to the dataset table for fresh schema creation.
sql/changelog.xml Registers migration 28.sql in the DB changelog.
frontend/src/app/dashboard/service/user/dataset/dataset.service.ts Adds shared client-side dataset name validation and updates the default dataset name to a valid value.
frontend/src/app/dashboard/service/user/dataset/dataset.service.spec.ts Adds unit tests for validateDatasetName.
frontend/src/app/dashboard/component/user/user-dataset/user-dataset-list-item/user-dataset-list-item.component.ts Validates dataset rename input client-side and shows backend error details via extractErrorMessage.
frontend/src/app/dashboard/component/user/user-dataset/user-dataset-list-item/user-dataset-list-item.component.spec.ts Adds tests for client-side rejection and backend-message surfacing during dataset rename.
frontend/src/app/dashboard/component/user/list-item/list-item.component.ts Validates dataset renames in the shared list-item UI and surfaces backend error messages.
frontend/src/app/dashboard/component/user/list-item/list-item.component.spec.ts Adds tests covering invalid/valid dataset renames and rename failure behavior.
frontend/src/app/dashboard/component/user/list-item/card-item/card-item.component.ts Applies the same dataset rename validation and improved error messaging in the card-item UI.
frontend/src/app/dashboard/component/user/list-item/card-item/card-item.component.spec.ts Adds tests for dataset rename validation and error handling in card-item.
file-service/src/test/scala/org/apache/texera/service/resource/DatasetResourceSpec.scala Adds regression tests for rename validation, duplicate handling, DB uniqueness enforcement, and race-condition translation.
file-service/src/main/scala/org/apache/texera/service/resource/DatasetResource.scala Enforces validation + duplicate checks on rename, wraps writes to translate unique-constraint violations into 400s, and aligns validation rules with DB limits.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@xuang7 xuang7 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! One comment: 28.sql silently renames pre-existing duplicate datasets. Since this is a user-visible data change, and workflows that reference a renamed dataset by path may silently resolve to the surviving dataset afterward, we could consider adding a DO block with a RAISE NOTICE to make these renames visible.

@kunwp1
kunwp1 added this pull request to the merge queue Jul 16, 2026
@xuang7
xuang7 removed this pull request from the merge queue due to a manual request Jul 16, 2026
@xuang7 xuang7 added release/v1.2 back porting to release/v1.2 and removed release/v1.2 back porting to release/v1.2 labels Jul 16, 2026
@xuang7
xuang7 enabled auto-merge July 16, 2026 23:55
@xuang7
xuang7 added this pull request to the merge queue Jul 17, 2026
Merged via the queue into apache:main with commit d1ac3a3 Jul 17, 2026
75 of 120 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ddl-change Changes to the TexeraDB DDL fix frontend Changes related to the frontend GUI platform Non-amber Scala service paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Renaming a dataset bypasses name validation and the duplicate name check

4 participants