Skip to content

Conversation

@mzabuawala
Copy link
Contributor

@mzabuawala mzabuawala commented Jan 31, 2026

This pull request improves how tables that use the OF TYPE clause (i.e., tables based on composite types) are handled in pgAdmin. The changes ensure that columns inherited from composite types are tracked for modifications, allowing the UI and generated SQL to correctly use the WITH OPTIONS syntax when defaults or constraints are changed. This results in more accurate CREATE TABLE statements and better support for editing inherited columns.

Enhancements for OF TYPE columns:

  • In __init__.py, columns inherited from composite types now have an inheritedfromtype field and store their original default and NOT NULL values, enabling the backend and UI to detect and compare modifications.
  • In columns/utils.py, the column parsing logic was updated to include inherited columns with modifications (such as changed defaults or NOT NULL constraints) in the final column list, marking them for WITH OPTIONS syntax in the generated SQL.

Improvements to CREATE TABLE SQL generation:

  • In all relevant SQL template files (create.sql for versions 11+, 12+, 14+, 16+, and default), columns inherited from composite types that are modified now use the WITH OPTIONS syntax, ensuring the generated SQL accurately reflects changes to defaults or constraints. [1] [2] [3] [4] [5]

Summary by CodeRabbit

  • New Features

    • Track columns inherited from composite types and preserve their original default and NOT NULL values to support safe edits.
  • Bug Fixes

    • Avoided a potential null-reference error during table schema initialization.
  • Improvements

    • CREATE TABLE SQL now renders modified OF TYPE columns using WITH OPTIONS and outputs clear comments for columns inherited from parent tables.

@coderabbitai
Copy link

coderabbitai bot commented Jan 31, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Backend records original DEFAULT/NOT NULL for columns inherited from composite types; frontend tracks inherited-type metadata and permits editing OF TYPE columns; CREATE templates render modified OF TYPE columns using WITH OPTIONS while parent-table inheritance remains comment-only.

Changes

Cohort / File(s) Summary
Backend type-column tracking
web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
When fetching OF TYPE columns, mark inheritedfromtype (preserve inheritedfrom) and add original_defval and original_attnotnull; append enhanced rows into res['oftype_columns'].
Column parsing & modification detection
web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/utils.py
In CREATE flow, extract PK list and detect modifications on inherited-from-type columns by comparing original_defval/original_attnotnull with current values; set has_with_options for modified inherited-type columns and include them in final_columns.
Frontend column UI changes
web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js
Add isInheritedFromType() and hidden fields inheritedfromtype, inheritedfromtable, original_defval, original_attnotnull. Update editable/default-value logic to allow editing OF TYPE columns but restrict parent-table-inherited edits.
SQL template rendering branches
web/pgadmin/.../templates/tables/sql/*/create.sql, web/pgadmin/.../templates/tables/sql/default/create.sql
Add branch to render columns with inheritedfromtype && has_with_options using WITH OPTIONS (include NOT NULL, DEFAULT, storage/compression where applicable); keep parent-table inheritance as a comment; fallback to existing rendering otherwise.
UI guard fix
web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js
Guard LikeSchema.resetVals with this.top && this.top.isNew() to avoid accessing undefined top.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User
  participant UI as Frontend (Column UI)
  participant API as Backend API
  participant Gen as SQL Template Generator

  User->>UI: Inspect or edit column metadata
  UI->>UI: isInheritedFromType(), read `original_*` hidden fields
  UI->>API: Fetch/submit column metadata
  API->>API: get_oftype() adds `original_defval`/`original_attnotnull`, marks `inheritedfromtype`
  API->>Gen: Provide column metadata (includes `has_with_options`, `inheritedfromtable`)
  Gen->>Gen: Choose rendering branch (WITH OPTIONS / inherited-from-table comment / regular)
  Gen-->>API: Return generated CREATE SQL
  API-->>UI: Send updated metadata and SQL
  UI-->>User: Display SQL and editing result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • anilsahoo20
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main objective: enabling users to customize columns inherited from composite types during table creation, which aligns with all the backend and UI changes across the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

@mzabuawala mzabuawala force-pushed the fixes_ofType_223 branch 2 times, most recently from c1e7467 to d63a40f Compare January 31, 2026 13:39
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In
`@web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js`:
- Around line 260-266: The map callback that builds inc_ids leaves undefined
entries when the if condition is false; update the logic around keys.map(...)
(the block assigning inc_ids) to only produce numeric ids for columns present in
currPk.include — for example replace the single map with a filter+map or a
reduce so you first filter keys where
currPk.include.indexOf(actionObj.oldState.columns[k].name) > -1 and then
parseInt(k), or keep the map but return null/undefined and immediately follow
with .filter(Boolean) to remove empties; ensure the final inc_ids contains only
parsed integers.
- Around line 244-247: The map callback for keys.map only returns when the if
condition is true, producing undefined entries; change the logic to produce a
compact array of ints by using filter+map or flatMap: e.g., filter keys where
columns.indexOf(actionObj.oldState.columns[k].name) > -1 then map to
parseInt(k), or use keys.flatMap(k =>
columns.indexOf(actionObj.oldState.columns[k].name) > -1 ? [parseInt(k)] : []).
Update the expression that assigns ids (the keys.map usage and variable ids)
accordingly so every execution path returns a defined value.

In
`@web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/tables/sql/16_plus/create.sql`:
- Line 57: The template can produce invalid SQL if a column is marked WITH
OPTIONS while also being an identity column, so update the backend logic that
computes has_with_options to explicitly skip setting it for identity
constraints: when determining has_with_options for OF TYPE columns (the code
path that currently sets has_with_options based on DEFAULT, NOT NULL, or PRIMARY
KEY changes), add a guard that if colconstype == 'i' (identity) do not set
has_with_options; ensure the same check is applied wherever has_with_options is
derived or mutated so templates using has_with_options can never see true for
identity columns.

In
`@web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/tables/sql/default/create.sql`:
- Line 49: The template has a malformed conditional around rendering the column
type: wrap the direct type rendering ({{c.displaytypname}}) with an {% if is_sql
%} ... {% else %} ... {% endif %} so the existing {% else %} and {% endif %}
pair up correctly; specifically, after rendering the column name
({{conn|qtIdent(c.name)}}) insert {% if is_sql %}{{c.displaytypname}}{% else %}
and keep the existing GET_TYPE.CREATE_TYPE_SQL(conn, c.cltype, c.attlen,
c.attprecision, c.hasSqrBracket) branch as the else, ensuring the final {% endif
%} closes this new conditional.
🧹 Nitpick comments (1)
web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/utils.py (1)

367-375: Unused pk_columns set is built but never referenced.

The pk_columns set is populated from data['primary_key'], but it's never used in the subsequent logic. The only reference is in a commented-out block (lines 389-391). Consider either:

  1. Removing this code if it's not needed
  2. Uncommenting the usage if primary key detection should trigger has_with_options
♻️ Option 1: Remove unused code
-        # Get list of columns in primary key constraint
-        pk_columns = set()
-        if 'primary_key' in data and len(data['primary_key']) > 0:
-            for pk in data['primary_key']:
-                if 'columns' in pk:
-                    for col in pk['columns']:
-                        if 'column' in col:
-                            pk_columns.add(col['column'])
-
         for c in columns:

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js`:
- Around line 163-165: The edit/readonly logic currently treats any
inheritedfrom as table inheritance; update those gates to distinguish type
inheritance by using isInheritedFromType(state) so only true table inheritance
makes fields read-only. Specifically, replace direct checks of
state.inheritedfrom in the edit/readonly guards (and the
length/precision/default validation paths) with a check that inheritedfrom is
present AND isInheritedFromType(state) is false (e.g., only treat as
table-inherited when !isInheritedFromType(state) &&
!isEmptyString(state.inheritedfrom)). Also ensure get_oftype still preserves
compatibility but that any logic deciding editability consults
isInheritedFromType(state) to allow OF TYPE columns to be editable.

In
`@web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/utils.py`:
- Around line 386-423: Remove the redundant is_primary_key-driven modification
flag: delete the check that sets has_modifications when c.get('is_primary_key')
is true so primary keys are not flagged as column-level changes; rely only on
actual DEFAULT/NOT NULL differences (original_defval/defval and
original_attnotnull/attnotnull) to set has_modifications, and keep the existing
identity check and has_with_options assignment for final_columns as-is (refer to
variables/keys c, is_primary_key, has_modifications, original_defval, defval,
original_attnotnull, attnotnull, has_with_options, final_columns).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/utils.py`:
- Around line 367-423: Remove the check that sets has_modifications when
c.get('is_primary_key') is true inside the columns loop so that PRIMARY KEY does
not cause has_with_options to be set; instead only detect modifications via
differences in defval (original_defval vs defval) and attnotnull
(original_attnotnull vs attnotnull) as currently implemented, and continue to
skip identity columns (colconstype == 'i') before marking c['has_with_options']
= True; locate this logic around the loop over columns and the variables
has_modifications, c.get('is_primary_key'), original_defval/current_defval, and
original_attnotnull/current_attnotnull.

@akshay-joshi
Copy link
Contributor

@mzabuawala

Fixed all CodeRabbit review comments if possible.

@mzabuawala mzabuawala force-pushed the fixes_ofType_223 branch 2 times, most recently from 7e5ed2d to 56b65fa Compare February 3, 2026 14:27
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js (1)

372-378: ⚠️ Potential issue | 🟠 Major

Length/precision editable checks still block OF TYPE columns.

The attlen and attprecision editable functions use raw state.inheritedfrom checks, which block editing for OF TYPE columns even though they should be editable. The defval field was already updated to use isInheritedFromType() to allow editing for type-inherited columns while blocking table-inherited ones.

Update these checks to match the defval pattern:

Suggested fix for attlen (lines 372-378)
       editable: function(state) {
-        // inheritedfrom has value then we should disable it
-        if (!isEmptyString(state.inheritedfrom)) {
+        // Block editing for table inheritance, but allow for type inheritance
+        if (!isEmptyString(state.inheritedfrom) && !obj.isInheritedFromType(state)) {
           return false;
         }
         return Boolean(obj.attlenRange(state));
       },
Suggested fix for attprecision (lines 404-410)
       editable: function(state) {
-        // inheritedfrom has value then we should disable it
-        if (!isEmptyString(state.inheritedfrom)) {
+        // Block editing for table inheritance, but allow for type inheritance
+        if (!isEmptyString(state.inheritedfrom) && !this.isInheritedFromType(state)) {
           return false;
         }
         return Boolean(this.attprecisionRange(state));
       },
🤖 Fix all issues with AI agents
In
`@web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/tables/sql/default/create.sql`:
- Line 49: The template has a mismatched Jinja2 conditional around rendering the
column type: after {{c.displaytypname}} there's an {% else %} with no matching
{% if %}, causing a syntax error; fix by replacing that fragment so the type
rendering is a proper if/else (i.e. test whether c.displaytypname is present and
render it, otherwise call GET_TYPE.CREATE_TYPE_SQL(conn, c.cltype, c.attlen,
c.attprecision, c.hasSqrBracket)), keeping the surrounding
inherited-from-type/parent-table branches intact and preserving references to
conn|qtIdent(c.name), c.inheritedfromtype, c.inheritedfromtable,
c.displaytypname and GET_TYPE.CREATE_TYPE_SQL.
🧹 Nitpick comments (2)
web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js (1)

493-518: Simplify the disabled logic for clarity.

Lines 495-497 have unusual control flow - the if block returns isDisabled = false but immediately exits with return isDisabled = false, which is redundant and confusing.

♻️ Suggested simplification
       disabled: function(state) {
         let isDisabled = ['serial', 'bigserial', 'smallserial'].indexOf(state.cltype) > -1;
-        if(!isDisabled && this.isInheritedFromType(state)) {
-          return isDisabled = false;
-        }
+        // Allow editing for OF TYPE columns
+        if(this.isInheritedFromType(state)) {
+          return false;
+        }
         isDisabled = isDisabled || state.colconstype != 'n';
         return isDisabled;
       },
web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/utils.py (1)

367-375: Remove dead code or add TODO comment.

The pk_columns set is built but never used (the usage is commented out on lines 390-391). This is dead code that adds unnecessary complexity.

Either remove this block entirely, or if it's intentionally preserved for future use, add a clear TODO comment explaining the intent.

♻️ Option 1: Remove dead code
-        # Get list of columns in primary key constraint
-        pk_columns = set()
-        if 'primary_key' in data and len(data['primary_key']) > 0:
-            for pk in data['primary_key']:
-                if 'columns' in pk:
-                    for col in pk['columns']:
-                        if 'column' in col:
-                            pk_columns.add(col['column'])
-
         for c in columns:

@mzabuawala
Copy link
Contributor Author

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)

web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js (1)> 372-378: ⚠️ Potential issue | 🟠 Major

Length/precision editable checks still block OF TYPE columns.
The attlen and attprecision editable functions use raw state.inheritedfrom checks, which block editing for OF TYPE columns even though they should be editable. The defval field was already updated to use isInheritedFromType() to allow editing for type-inherited columns while blocking table-inherited ones.
Update these checks to match the defval pattern:

Suggested fix for attlen (lines 372-378)

       editable: function(state) {
-        // inheritedfrom has value then we should disable it
-        if (!isEmptyString(state.inheritedfrom)) {
+        // Block editing for table inheritance, but allow for type inheritance
+        if (!isEmptyString(state.inheritedfrom) && !obj.isInheritedFromType(state)) {
           return false;
         }
         return Boolean(obj.attlenRange(state));
       },

Suggested fix for attprecision (lines 404-410)

       editable: function(state) {
-        // inheritedfrom has value then we should disable it
-        if (!isEmptyString(state.inheritedfrom)) {
+        // Block editing for table inheritance, but allow for type inheritance
+        if (!isEmptyString(state.inheritedfrom) && !this.isInheritedFromType(state)) {
           return false;
         }
         return Boolean(this.attprecisionRange(state));
       },

🤖 Fix all issues with AI agents

In
`@web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/tables/sql/default/create.sql`:
- Line 49: The template has a mismatched Jinja2 conditional around rendering the
column type: after {{c.displaytypname}} there's an {% else %} with no matching
{% if %}, causing a syntax error; fix by replacing that fragment so the type
rendering is a proper if/else (i.e. test whether c.displaytypname is present and
render it, otherwise call GET_TYPE.CREATE_TYPE_SQL(conn, c.cltype, c.attlen,
c.attprecision, c.hasSqrBracket)), keeping the surrounding
inherited-from-type/parent-table branches intact and preserving references to
conn|qtIdent(c.name), c.inheritedfromtype, c.inheritedfromtable,
c.displaytypname and GET_TYPE.CREATE_TYPE_SQL.

🧹 Nitpick comments (2)

A composite type has its structure (data types, length, precision, scale, etc.) fully fixed at type definition time, so kept them as is.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js (1)

493-518: ⚠️ Potential issue | 🟡 Minor

Keep the colconstype guard even for OF TYPE columns.

The early return when isInheritedFromType() is true skips the colconstype != 'n' check, so defaults can be edited for identity/generated columns, which can yield invalid SQL. Removing that early return preserves the intended OF TYPE enablement while keeping the identity/generated guard.

🛠️ Suggested fix
       disabled: function(state) {
         let isDisabled = ['serial', 'bigserial', 'smallserial'].indexOf(state.cltype) > -1;
-        if(!isDisabled && this.isInheritedFromType(state)) {
-          return isDisabled = false;
-        }
         isDisabled = isDisabled || state.colconstype != 'n';
         return isDisabled;
       },

@mzabuawala mzabuawala changed the title Allow users to customize ofType columns during table creation #223 Allow users to customize ofType columns during table creation #229 Feb 10, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js`:
- Around line 493-499: The guard in the disabled function is a no-op because it
checks !isDisabled then assigns and returns false, which never alters a true
disabled state; update the logic in disabled (in the same function that calls
isInheritedFromType) to: if the intention is to force-enable OF TYPE columns,
change the guard to check for isDisabled && this.isInheritedFromType(state) and
return false to override the disable; otherwise remove the entire if-block that
references isInheritedFromType(state) to eliminate dead code so the function
simply returns the computed isDisabled value.
🧹 Nitpick comments (2)
web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/utils.py (1)

367-374: pk_columns is computed but never used.

The set is populated but the only code that references it (lines 389–391) is commented out. This is dead code that adds unnecessary computation and may confuse future maintainers.

♻️ Suggested cleanup
-        # Get list of columns in primary key constraint
-        pk_columns = set()
-        if 'primary_key' in data and len(data['primary_key']) > 0:
-            for pk in data['primary_key']:
-                if 'columns' in pk:
-                    for col in pk['columns']:
-                        if 'column' in col:
-                            pk_columns.add(col['column'])
-
         for c in columns:

Also remove the commented-out block at lines 386–391 that references pk_columns.

web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js (1)

506-508: this vs obj inconsistency in depChange arrow function.

Line 507 mixes obj.isNew(state) (closure) with this.isInheritedFromType(state) (lexical this from the arrow function). Both resolve to the same ColumnSchema instance here, but the inconsistency within a single expression is a readability concern. Consider using obj consistently (as done elsewhere in arrow-function callbacks in this file) or this consistently.

@khushboovashi khushboovashi merged commit b366d21 into pgadmin-org:master Feb 10, 2026
37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants