> ## Documentation Index
> Fetch the complete documentation index at: https://ngquct-feat-table-properties-tab.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Change Tracking

> Queue cell edits, row inserts and deletions, review the SQL, then save

export const what_0 = "Cell edits, inserts and deletes"

Nothing here reaches the server on its own. {what_0} queue on the tab, the toolbar counts them,
**Preview SQL** (`Cmd+Shift+P`) shows the statements they will produce, and `Cmd+S` runs them.

The toolbar counts what is pending, modified cells stay highlighted, `Cmd+Shift+P` shows the statements the queue will produce, and `Cmd+S` runs them. The queue belongs to the tab, so switching tabs leaves it alone.

<Frame caption="Data grid with pending changes highlighted">
  <img className="block dark:hidden" src="https://mintcdn.com/ngquct-feat-table-properties-tab/Ae_HE8knctkrWDpl/images/change-tracking.png?fit=max&auto=format&n=Ae_HE8knctkrWDpl&q=85&s=bc180525d52aa8f976a461a092f6623c" alt="Pending edits, inserts, and deletions highlighted in the data grid" width="1560" height="960" data-path="images/change-tracking.png" />

  <img className="hidden dark:block" src="https://mintcdn.com/ngquct-feat-table-properties-tab/Ae_HE8knctkrWDpl/images/change-tracking-dark.png?fit=max&auto=format&n=Ae_HE8knctkrWDpl&q=85&s=664d1793d4dbfcc0d3f3b19a9c24c878" alt="Pending edits, inserts, and deletions highlighted in the data grid" width="1560" height="960" data-path="images/change-tracking-dark.png" />
</Frame>

## Editing a cell

Double-click a cell, or press `Enter` on it. `Enter` commits the edit to the queue, `Escape` cancels. Setting a value back to what it was drops it from the queue again. Some types open an editor of their own:

| Column type                         | Editor                                            |
| ----------------------------------- | ------------------------------------------------- |
| Date, time, datetime, timestamp     | Calendar and time picker, or edit as text         |
| Foreign key                         | Searchable list of referenced values              |
| Boolean, `BIT`, `TINYINT(1)`        | Checkbox, with a third state for nullable columns |
| `ENUM`                              | Searchable value list                             |
| `SET` (MySQL/MariaDB)               | Multi-select checkboxes                           |
| Array of a simple type (PostgreSQL) | Ordered list, one row per element                 |
| `JSON`, `JSONB`, `BLOB`, binary     | [Cell viewers](/features/json-viewer)             |

<Frame caption="Type-specific cell editor">
  <img className="block dark:hidden" src="https://mintcdn.com/ngquct-feat-table-properties-tab/Ae_HE8knctkrWDpl/images/date-picker-editor.png?fit=max&auto=format&n=Ae_HE8knctkrWDpl&q=85&s=1d39f1d22518416b774dcd79311d7be1" alt="Cell editor" width="1560" height="960" data-path="images/date-picker-editor.png" />

  <img className="hidden dark:block" src="https://mintcdn.com/ngquct-feat-table-properties-tab/Ae_HE8knctkrWDpl/images/date-picker-editor-dark.png?fit=max&auto=format&n=Ae_HE8knctkrWDpl&q=85&s=9d8ae41752994b0a6e6cf819e8fd8ff2" alt="Cell editor" width="1560" height="960" data-path="images/date-picker-editor-dark.png" />
</Frame>

Right-click an editable cell and open **Set Value** for the common ones without typing: **Empty**, **NULL** on a nullable column, **Default** on a column that has one, and date functions such as `NOW()` or `CURRENT_DATE` on date columns.

## Adding and deleting rows

* **Add Row** (`Cmd+Shift+N`), the **+** button, or **Edit > Add Row**. The row appears at the bottom, columns with a default pre-filled as `DEFAULT`. Later edits to it fold into the INSERT rather than queueing as updates.
* **Duplicate Row** (`Cmd+Shift+D`) on the right-click menu copies a row and resets its primary key to `DEFAULT`, so the database assigns a new one.
* **Delete**: select rows by their row numbers (`Shift`-click for a range, `Cmd`-click for separate rows) and press `Delete`. They stay visible with a strikethrough, and saving asks "Delete 5 rows?" before they go.
* **Paste** on the right-click menu inserts copied rows as new rows.
* **Fill Column** on the header right-click menu writes one value into every loaded row, skipping primary key columns.

## When the grid will not edit

A tab opened from the sidebar edits its table directly. A query tab edits only when the app can prove the rows came from exactly one table:

| Query                                                                                       | Editable          |
| ------------------------------------------------------------------------------------------- | ----------------- |
| `SELECT * FROM orders o WHERE o.id = 1`                                                     | Yes, alias or not |
| Comments, `DISTINCT`, `ORDER BY`, `LIMIT`, `FOR UPDATE`                                     | Yes               |
| `SELECT * FROM public.orders` on the schema you are browsing                                | Yes               |
| The same query naming any other schema                                                      | No                |
| Joins, comma joins, subqueries in `FROM`, CTEs, `UNION`, `EXCEPT`, `INTERSECT`, `FROM ONLY` | No                |

To change rows a join returned, open one of its tables from the sidebar and edit there. To edit through another schema, switch the session to that schema first.

Two more shapes look editable and refuse. A column renamed with `AS` cannot be written back, though the rest of the row still can. A query that renames or omits the primary key blocks the save outright, because nothing is left to identify the row by.

Individual columns stay read-only where the server owns them: generated columns, which are computed on write and left out of every INSERT and UPDATE, SQL export included, and columns the driver marks immutable, such as MongoDB's `_id`. A connection set to the Read-Only [safe mode](/features/safe-mode) level edits nothing at all.

## Saving

Press `Cmd+S`, or click the toolbar checkmark. Values go out as bound parameters; `DEFAULT` and SQL functions such as `NOW()` are written into the statement as they stand.

| Change        | Generated SQL                           |
| ------------- | --------------------------------------- |
| Cell edit     | `UPDATE … SET column = ? WHERE pk = ?`  |
| Row insertion | `INSERT INTO … (columns) VALUES (?, …)` |
| Row deletion  | `DELETE FROM … WHERE pk = ? OR pk = ?`  |

A table without a primary key is matched on every original column value instead, with `IS NULL` for the nulls.

On an engine with transactions, the statements run inside one, so a failure rolls the whole save back and the table is left as it was. Without them, the statements that ran before the failure stand. Either way a failed save reports **Save Failed** with the server's message and keeps the queue intact, so correct the value and save again. A save that succeeds clears the queue, clears undo, and reloads the grid.

Each statement is held to the number of rows it was written for. On a table with no primary key, two identical rows cannot be told apart, so a statement meant for one of them matches both; the save stops there and reports what happened instead of rewriting the other row.

There is no discard button. Undo the edits, or take the **Discard Unsaved Changes?** prompt that appears when you refresh, sort, filter, change page, or close the tab with edits pending.

## Previewing the SQL

`Cmd+Shift+P`, or the eye button in the toolbar, lists the statements with their parameter values inlined, so the preview reads as what will run. **Copy All** copies every statement.

## Undo and redo

`Cmd+Z` and `Cmd+Shift+Z`. Focus decides what they act on: text in the SQL editor, queued changes in the grid. Deleting several rows undoes as one step. The stacks are per tab, and a save clears them.

## Restoring a save

**Edit > Restore Previous Values…**, or the same item in the toolbar's Table Actions group, puts back what the rows held before the last save on the current table. Starter license.

Values are kept on this Mac for 7 days, encrypted, and never synced. Turn the capture off, or delete what is stored, in **Settings > Data & Results**.

Every row is planned against the table as it stands now, and the sheet says what will happen to each one before anything runs:

| Outcome                            | Means                                                                                   |
| ---------------------------------- | --------------------------------------------------------------------------------------- |
| Will restore                       | The row still holds what the save wrote                                                 |
| Changed since the save             | Someone else wrote to the same column, and the row is left alone                        |
| Already restored                   | The row is back at its old value, so a rewind that stopped halfway is safe to run again |
| Row no longer exists               | Nothing left to write to                                                                |
| A row with this key already exists | Something took the deleted row's key                                                    |

**Restore** writes the changes back the way any other save does: through the same [safe mode](/features/safe-mode) prompt, in one transaction where the engine has them, and into query history. The restore is itself restorable.

<Warning>
  Only what TablePro wrote is restored. Triggers that fired, audit rows they wrote, and rows a cascade deleted stay as they are.
</Warning>

A save is not restorable at all when it also truncated or dropped a table. Within a save, a row is skipped when the table has no primary key, when a column was set to `DEFAULT` or a function such as `NOW()` whose stored value the app never read back, when the server chose the key of an inserted row, or when a value was larger than 1 MB.

## Structure changes

Column, index, foreign key, and primary key edits queue in the same place and go out with the same Save, previewed as DDL instead of DML. See [Table Structure](/features/table-structure).
