Summary
When staged changes are saved in Table Browser (Postgres / MySQL / SQLite), DataGridStagingBuffer.effectiveRows is used to populate the new baseline rows. However:
- Deleted rows reappear:
effectiveRows iterates over all _originalRows without filtering out _deletedRowIndices. Even though DELETE FROM ... succeeds in the database, the deleted row is preserved in newRows and put back into the grid as an active clean row until a manual refresh.
kNullSentinel leakage: Cells set to NULL contain TableMutationEngine.kNullSentinel (\u0000__QUERYA_NULL__\u0000). effectiveRows copies this raw sentinel into the row strings instead of sanitizing it to 'NULL', leaking control characters into subsequent grid renders and exports.
Repro
- Open Table Browser on any table with rows (Postgres / MySQL / SQLite).
- Right-click a row and select Delete Row (row shows strikethrough and pending badge).
- Click Save and confirm the DML execution in the dialog.
- Expected: The deleted row disappears from the grid.
- Actual: Toast reports "1 change(s) saved", but the deleted row is still displayed in the grid as an active row.
- Similarly, set any cell to NULL (
Alt+N), Save, and observe raw \u0000__QUERYA_NULL__\u0000 leaking into effective rows / exports.
Scope
• Update DataGridStagingBuffer.effectiveRows (or a dedicated post-save committed snapshot) to strictly exclude indices in _deletedRowIndices.
• Ensure any staged kNullSentinel values in cells and inserted rows are mapped to 'NULL' when building effective rows.
• Verify post-save state in postgres_table_view.dart, mysql_table_view.dart, and sqlite_table_view.dart.
• Add regression tests in test/features/workspace/data_grid_staging_buffer_test.dart and test/features/workspace/table_view_staging_test.dart.
Out of scope
• RETURNING clause / reloading auto-increment IDs on INSERT (separate issue).
• Optimistic concurrency column-level validation (separate issue).
Summary
When staged changes are saved in Table Browser (Postgres / MySQL / SQLite),
DataGridStagingBuffer.effectiveRowsis used to populate the new baseline rows. However:effectiveRowsiterates over all_originalRowswithout filtering out_deletedRowIndices. Even thoughDELETE FROM ...succeeds in the database, the deleted row is preserved innewRowsand put back into the grid as an active clean row until a manual refresh.kNullSentinelleakage: Cells set to NULL containTableMutationEngine.kNullSentinel(\u0000__QUERYA_NULL__\u0000).effectiveRowscopies this raw sentinel into the row strings instead of sanitizing it to'NULL', leaking control characters into subsequent grid renders and exports.Repro
Alt+N), Save, and observe raw\u0000__QUERYA_NULL__\u0000leaking into effective rows / exports.Scope
• Update
DataGridStagingBuffer.effectiveRows(or a dedicated post-save committed snapshot) to strictly exclude indices in_deletedRowIndices.• Ensure any staged
kNullSentinelvalues in cells and inserted rows are mapped to'NULL'when building effective rows.• Verify post-save state in
postgres_table_view.dart,mysql_table_view.dart, andsqlite_table_view.dart.• Add regression tests in
test/features/workspace/data_grid_staging_buffer_test.dartandtest/features/workspace/table_view_staging_test.dart.Out of scope
• RETURNING clause / reloading auto-increment IDs on INSERT (separate issue).
• Optimistic concurrency column-level validation (separate issue).