Skip to content

Fix inherited-column read-only state and Definition-tab type restriction in the Table dialog - #10332

Open
dpage wants to merge 7 commits into
pgadmin-org:masterfrom
dpage:fix/10179-inherited-columns
Open

Fix inherited-column read-only state and Definition-tab type restriction in the Table dialog#10332
dpage wants to merge 7 commits into
pgadmin-org:masterfrom
dpage:fix/10179-inherited-columns

Conversation

@dpage

@dpage dpage commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

What this is

Two related bugs in the Table Create/Properties dialog's Columns grid.

Inherited columns editable/deletable (#10179). column.ui.js's inSchemaWithColumnCheck and table.ui.js's canEditDeleteRowColumns had two mismatches:

  • When a table is opened already inheriting, the properties fetch sets inheritedfromtable on inherited columns, but the frontend only ever checked inheritedfrom — so those rows were never recognised as inherited.
  • When a parent is added interactively via "Inherited from table(s)", the fetched rows do carry inheritedfrom, but carry no attnum yet, and the inherited check ran after an isNew() short-circuit — so these attnum-less rows were still treated as fully editable/deletable.

Removal-on-detach already worked via the existing deferredDepChange pruning logic, confirmed by pre-existing tests, so no change was needed there.

Definition-tab type dropdown ignoring the restriction (#10180). The inline grid-cell editor and the expanded row's Definition tab each had their own copy of the edit_types filter for cltype, but MappedControl resolves a field's type() callback against the whole table's data rather than the field's own row (unlike cell(), which already gets the row) — so the tab's isNew()/edit_types checks always read the wrong object and no-opped, showing every type.

Fixes #10179, fixes #10180.

Fix

  • Check inheritedfrom/inheritedfromtable before the isNew() short-circuit in both inSchemaWithColumnCheck and canEditDeleteRowColumns.
  • MappedControl now resolves a field's declared deps against its own row (as listenDepChanges already does for cell) and forwards them as a second argument to type(). column.ui.js declares edit_types/attnum as deps on cltype and factors the filter into one shared editTypesFilter() used by both cell and type, so the two can't drift apart again.

Test plan

  • Extended column.ui.spec.js and table.ui.spec.js to cover both cases.
  • yarn run jest schema_ui_files/table.ui.spec.js schema_ui_files/column.ui.spec.js schema_ui_files/catalog_object_column.ui.spec.js — 36/36 passed.
  • Full test:js-once (152 suites / 948 tests) passed with the MappedControl.jsx change in place.
  • eslint clean on all changed files.

dpage added 7 commits August 19, 2026 11:30
…-org#10214)

The annotation conditions referenced the non-existent top-level
.Values.existingSecret instead of .Values.auth.existingSecret, so the
secret checksum annotation and the empty-secret gating never worked as
intended when an existing secret wasn't supplied.
…rg#10251)

CONCURRENTLY was being appended to the parenthesised option list
alongside VERBOSE etc., which PostgreSQL rejects. It's not a
parenthesizable option: it belongs standalone, between the object
type keyword and the object name.
…min-org#9450)

A role's membership tab only enabled the add/remove member controls
for superusers and CREATEROLE holders, so a user who was themselves
granted ADMIN OPTION on that role (and can therefore GRANT/REVOKE its
membership at the SQL level) had no way to add other members, and hit
a permission error server-side if they tried anyway.

The role UI schema now also allows membership changes when the
current user is a member of the role with admin=true. The backend
mirrors this: permission.sql reports whether the connecting user
holds ADMIN OPTION on the target role, and the update handler lets
such a request through only when it's restricted to rolmembers
changes, so this can't be used to escalate other role attributes.
…in-org#10309)

validate_json_data() only checked that the Username key was present on
a non-shared server, not that it held anything useful, so an empty or
null value imported cleanly and left behind a server that libpq would
silently authenticate as the OS account running pgAdmin rather than
reject outright. Check the value, matching the truthiness check already
used for shared servers and the "Username must be specified" rule
enforced by the server dialog.
…mode (pgadmin-org#8991)

execute_void() blindly reused whatever cursor was cached for the
connection, which under "server cursor" mode is the named/server-side
AsyncDictServerCursor left over from the last SELECT. A named cursor's
execute() always wraps the statement as `DECLARE ... CURSOR FOR
<query>`, which cannot express a transaction-control statement, so
BEGIN/COMMIT/ROLLBACK silently failed (failing one step earlier still,
on a `prepare` keyword the server-side cursor's execute() doesn't
accept at all) and the exception was swallowed by the background query
thread. The transaction was therefore never actually committed or
rolled back, and the next poll() picked up the previous query's
leftover column info, which is what made the result grid appear
instead of the Messages tab.

Run the statement through a throwaway plain cursor instead, leaving
the cached server-side cursor untouched, and clear the stale column
info so poll() correctly reports no result set.
Backend properties fetch marks a column already inherited from a
parent table with inheritedfromtable, while a column fetched
interactively via 'Inherited from table(s)' carries inheritedfrom
instead and has no attnum yet. inSchemaWithColumnCheck only checked
inheritedfrom, and did so after an isNew() short-circuit that treated
the attnum-less interactive rows as new, so inherited columns ended up
editable and deletable in both cases. Check both fields, and check
them before the isNew() short-circuit, and extend
canEditDeleteRowColumns the same way so the row's edit/delete buttons
are disabled too.
…-org#10180)

The expanded row's Definition tab and the inline grid-cell editor each
defined their own copy of the edit_types filter for the 'cltype'
field, but the tab's version received the whole table's data as
'state' rather than the row, since MappedControl resolves a field's
'type' callback against the top-level schema, not the field's own row
(unlike 'cell', which already gets the full row). That made isNew()
and edit_types resolve against the wrong object, so the filter always
no-opped and the tab showed every type instead of the restricted set.

Have MappedControl also resolve a field's declared 'deps' against its
own row (listenDepChanges already does this correctly for 'cell') and
forward them as a 2nd argument to 'type', mirroring what 'cell'
already receives. column.ui.js declares 'edit_types'/'attnum' as deps
on 'cltype' and factors the filter into one shared editTypesFilter()
used by both 'cell' and 'type', so the two stay in sync by
construction.
@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@dpage, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 11 minutes

Limit details: You’ve used all 8 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 474ecbd6-4132-4737-8d2b-cd0948738c3b

📥 Commits

Reviewing files that changed from the base of the PR and between 0ebefaf and 77f7464.

📒 Files selected for processing (17)
  • pkg/helm/templates/deployment.yaml
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js
  • web/pgadmin/browser/server_groups/servers/roles/__init__.py
  • web/pgadmin/browser/server_groups/servers/roles/static/js/role.ui.js
  • web/pgadmin/browser/server_groups/servers/roles/templates/roles/sql/default/permission.sql
  • web/pgadmin/browser/server_groups/servers/roles/tests/test_role_check_permission_unit_test.py
  • web/pgadmin/static/js/SchemaView/MappedControl.jsx
  • web/pgadmin/tools/maintenance/templates/maintenance/sql/command.sql
  • web/pgadmin/tools/maintenance/tests/test_maintenance_create_job_unit_test.py
  • web/pgadmin/utils/__init__.py
  • web/pgadmin/utils/driver/psycopg3/connection.py
  • web/pgadmin/utils/driver/psycopg3/tests/test_execute_void_server_cursor.py
  • web/pgadmin/utils/tests/test_validate_json_data.py
  • web/regression/javascript/schema_ui_files/column.ui.spec.js
  • web/regression/javascript/schema_ui_files/role.ui.spec.js
  • web/regression/javascript/schema_ui_files/table.ui.spec.js

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant