mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2026-08-17 16:34:44 -05:00
* Propagate column renames to FK and unique constraints. #9060 In the new-table dialog, the primary key already updated its column references when a column was renamed, but foreign key and unique constraint definitions did not, leaving them pointing at the old name. Mirror the PK rename-propagation in the foreign_key and unique_constraint depChange handlers (and add 'columns' to the unique constraint deps so it fires on column changes). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Address review feedback for column rename propagation (#9060) - Remap unique constraint INCLUDE columns on rename. The INCLUDE list holds bare name strings (not {column} objects), so renaming an included column previously emitted stale DDL. Now mirrors the primary key INCLUDE handling. - Add regression tests covering rename propagation in depChange for foreign_key and unique_constraint, including the unique constraint INCLUDE case. - Correct the release note wording from "Create/Edit Table" to "Create Table"; the propagation only runs on the new-table path (state.oid === undefined). --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Ashesh Vashi <ashesh.vashi@enterprisedb.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
Ashesh Vashi
parent
6de0f371b2
commit
0b4bb3ea3f
@@ -44,6 +44,7 @@ Bug fixes
|
||||
| `Issue #6308 <https://github.com/pgadmin-org/pgadmin4/issues/6308>`_ - Fix the infinite loading spinner after an idle database connection is silently dropped, by detecting stale connections and offering a reconnect dialog.
|
||||
| `Issue #7596 <https://github.com/pgadmin-org/pgadmin4/issues/7596>`_ - Fix the Query Tool turning into a blank white screen when the runtime has a malformed default locale, by guarding the Query History date/time formatting against the resulting RangeError.
|
||||
| `Issue #8318 <https://github.com/pgadmin-org/pgadmin4/issues/8318>`_ - Fixed an error ("i.default.find(...) is undefined") that prevented deleting a table or relationship link in the ERD tool when a foreign key referenced a column that had been renamed.
|
||||
| `Issue #9060 <https://github.com/pgadmin-org/pgadmin4/issues/9060>`_ - Fixed an issue in the Create Table dialog where renaming a column did not update the column references in foreign key and unique constraint definitions for the new table.
|
||||
| `Issue #9091 <https://github.com/pgadmin-org/pgadmin4/issues/9091>`_ - Fix the Query Tool re-prompting for an unsaved password in a loop and rejecting the re-entered password, by caching the entered password on the server manager when the primary connection is already established.
|
||||
| `Issue #9128 <https://github.com/pgadmin-org/pgadmin4/issues/9128>`_ - Fixed an issue where the object breadcrumbs popup blocked clicks on the object explorer items beneath it.
|
||||
| `Issue #9595 <https://github.com/pgadmin-org/pgadmin4/issues/9595>`_ - Fix missing ALTER ... SET DEFAULT statements for inherited columns in the generated table SQL/EDIT script.
|
||||
|
||||
+34
-3
@@ -200,10 +200,25 @@ export class ConstraintsSchema extends BaseUISchema {
|
||||
disabled: this.inCatalog,
|
||||
canAddRow: obj.anyColumnAdded,
|
||||
expandEditOnAdd: true,
|
||||
depChange: (state)=>{
|
||||
depChange: (state, source, topState, actionObj)=>{
|
||||
if (state.is_partitioned && obj.top.getServerVersion() < 110000 || state.columns?.length <= 0) {
|
||||
return {foreign_key: []};
|
||||
}
|
||||
/* If a column is renamed, sync the foreign key local column references. #9060 */
|
||||
if(actionObj.type == SCHEMA_STATE_ACTIONS.SET_VALUE && actionObj.path[0] == 'columns' &&
|
||||
actionObj.path[actionObj.path.length-1] == 'name' && state.oid === undefined &&
|
||||
state.foreign_key?.length) {
|
||||
let oldName = actionObj.oldState.columns[actionObj.path[1]]?.name,
|
||||
newName = _.get(state, _.slice(actionObj.path, 0, -1))?.name;
|
||||
if(oldName && newName && oldName !== newName) {
|
||||
return {foreign_key: state.foreign_key.map((fk)=>({
|
||||
...fk,
|
||||
columns: fk.columns?.map((c)=>(
|
||||
c.local_column === oldName ? {...c, local_column: newName} : c
|
||||
)),
|
||||
}))};
|
||||
}
|
||||
}
|
||||
}
|
||||
},{
|
||||
id: 'check_group', type: 'group', label: gettext('Check'), visible: !this.inErd,
|
||||
@@ -223,7 +238,7 @@ export class ConstraintsSchema extends BaseUISchema {
|
||||
schema: this.uniqueConsObj,
|
||||
editable: false, type: 'collection',
|
||||
group: 'unique_group', mode: ['edit', 'create'],
|
||||
canEdit: true, canDelete: true, deps:['is_partitioned', 'typname'],
|
||||
canEdit: true, canDelete: true, deps:['is_partitioned', 'typname', 'columns'],
|
||||
columns : ['name', 'columns'],
|
||||
disabled: this.inCatalog,
|
||||
canAdd: function(state) {
|
||||
@@ -231,10 +246,26 @@ export class ConstraintsSchema extends BaseUISchema {
|
||||
},
|
||||
canAddRow: obj.anyColumnAdded,
|
||||
expandEditOnAdd: true,
|
||||
depChange: (state)=>{
|
||||
depChange: (state, source, topState, actionObj)=>{
|
||||
if (state.is_partitioned && obj.top.getServerVersion() < 110000 || state.columns?.length <= 0) {
|
||||
return {unique_constraint: []};
|
||||
}
|
||||
/* If a column is renamed, sync the unique constraint column references. #9060 */
|
||||
if(actionObj.type == SCHEMA_STATE_ACTIONS.SET_VALUE && actionObj.path[0] == 'columns' &&
|
||||
actionObj.path[actionObj.path.length-1] == 'name' && state.oid === undefined &&
|
||||
state.unique_constraint?.length) {
|
||||
let oldName = actionObj.oldState.columns[actionObj.path[1]]?.name,
|
||||
newName = _.get(state, _.slice(actionObj.path, 0, -1))?.name;
|
||||
if(oldName && newName && oldName !== newName) {
|
||||
return {unique_constraint: state.unique_constraint.map((uc)=>({
|
||||
...uc,
|
||||
columns: uc.columns?.map((c)=>(
|
||||
c.column === oldName ? {...c, column: newName} : c
|
||||
)),
|
||||
include: uc.include?.map((c)=>(c === oldName ? newName : c)),
|
||||
}))};
|
||||
}
|
||||
}
|
||||
}
|
||||
},{
|
||||
id: 'exclude_group', type: 'group', label: gettext('Exclude'), visible: !this.inErd,
|
||||
|
||||
@@ -298,6 +298,89 @@ describe('TableSchema', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('depChange column rename propagation', () => {
|
||||
jest.spyOn(schemaObj, 'getServerVersion').mockReturnValue(110000);
|
||||
schemaObj.constraintsObj.top = schemaObj;
|
||||
|
||||
let actionObj = {
|
||||
type: SCHEMA_STATE_ACTIONS.SET_VALUE,
|
||||
path: ['columns', 0, 'name'],
|
||||
oldState: {
|
||||
columns: [{ name: 'old_col' }],
|
||||
},
|
||||
};
|
||||
|
||||
// Foreign key local_column should be renamed (Create path only).
|
||||
let fkState = {
|
||||
oid: undefined,
|
||||
is_partitioned: false,
|
||||
columns: [{ name: 'new_col' }],
|
||||
foreign_key: [{
|
||||
name: 'fk1',
|
||||
columns: [
|
||||
{ local_column: 'old_col', referenced: 'r1' },
|
||||
{ local_column: 'other', referenced: 'r2' },
|
||||
],
|
||||
}],
|
||||
};
|
||||
expect(getFieldDepChange(schemaObj.constraintsObj, 'foreign_key')(
|
||||
fkState, ['columns'], null, actionObj)).toEqual({
|
||||
foreign_key: [{
|
||||
name: 'fk1',
|
||||
columns: [
|
||||
{ local_column: 'new_col', referenced: 'r1' },
|
||||
{ local_column: 'other', referenced: 'r2' },
|
||||
],
|
||||
}],
|
||||
});
|
||||
|
||||
// Unique constraint columns should be renamed.
|
||||
let ucState = {
|
||||
oid: undefined,
|
||||
is_partitioned: false,
|
||||
columns: [{ name: 'new_col' }],
|
||||
unique_constraint: [{
|
||||
name: 'uc1',
|
||||
columns: [
|
||||
{ column: 'old_col' },
|
||||
{ column: 'other' },
|
||||
],
|
||||
include: ['inc1', 'inc2'],
|
||||
}],
|
||||
};
|
||||
expect(getFieldDepChange(schemaObj.constraintsObj, 'unique_constraint')(
|
||||
ucState, ['columns'], null, actionObj)).toEqual({
|
||||
unique_constraint: [{
|
||||
name: 'uc1',
|
||||
columns: [
|
||||
{ column: 'new_col' },
|
||||
{ column: 'other' },
|
||||
],
|
||||
include: ['inc1', 'inc2'],
|
||||
}],
|
||||
});
|
||||
|
||||
// Unique constraint INCLUDE list should be renamed too (Fix #9060).
|
||||
let ucIncludeState = {
|
||||
oid: undefined,
|
||||
is_partitioned: false,
|
||||
columns: [{ name: 'new_col' }],
|
||||
unique_constraint: [{
|
||||
name: 'uc2',
|
||||
columns: [{ column: 'keycol' }],
|
||||
include: ['old_col', 'inc2'],
|
||||
}],
|
||||
};
|
||||
expect(getFieldDepChange(schemaObj.constraintsObj, 'unique_constraint')(
|
||||
ucIncludeState, ['columns'], null, actionObj)).toEqual({
|
||||
unique_constraint: [{
|
||||
name: 'uc2',
|
||||
columns: [{ column: 'keycol' }],
|
||||
include: ['new_col', 'inc2'],
|
||||
}],
|
||||
});
|
||||
});
|
||||
|
||||
it('validate', () => {
|
||||
let state = {is_partitioned: true};
|
||||
let setError = jest.fn();
|
||||
|
||||
Reference in New Issue
Block a user