Browse Source

Merge pull request #9684 from nocodb/nc-fix/9677-avoid-deleting-meta-on-desc-update

fix: Avoid deleting meta on LTAR description update - WIP
pull/9755/head
Mert E. 4 weeks ago committed by GitHub
parent
commit
ae6700fc1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 18
      packages/nocodb/src/models/Column.ts
  2. 4
      packages/nocodb/src/version-upgrader/NcUpgrader.ts
  3. 0
      packages/nocodb/src/version-upgrader/upgraders/0227000_dupColMergeUpgrader.ts
  4. 297
      packages/nocodb/src/version-upgrader/upgraders/0227002_ncBrokenLinkRecovery.ts

18
packages/nocodb/src/models/Column.ts

@ -1112,7 +1112,7 @@ export default class Column<T = any> implements ColumnType {
skipFormulaInvalidate = false, skipFormulaInvalidate = false,
) { ) {
const oldCol = await Column.get(context, { colId }, ncMeta); const oldCol = await Column.get(context, { colId }, ncMeta);
let insertColOpt = true;
switch (oldCol.uidt) { switch (oldCol.uidt) {
case UITypes.Lookup: { case UITypes.Lookup: {
// LookupColumn.insert() // LookupColumn.insert()
@ -1147,7 +1147,21 @@ export default class Column<T = any> implements ColumnType {
break; break;
} }
case UITypes.Links:
case UITypes.LinkToAnotherRecord: { case UITypes.LinkToAnotherRecord: {
// delete only if all required fields are present
if (
[
'type',
'fk_child_column_id',
'fk_parent_column_id',
'fk_related_model_id',
].some((k) => !column[k])
) {
insertColOpt = false;
break;
}
await ncMeta.metaDelete( await ncMeta.metaDelete(
context.workspace_id, context.workspace_id,
context.base_id, context.base_id,
@ -1347,6 +1361,8 @@ export default class Column<T = any> implements ColumnType {
prepareForResponse(updateObj), prepareForResponse(updateObj),
); );
// insert new col options only if existing colOption meta is deleted
if (insertColOpt)
await this.insertColOption(context, column, colId, ncMeta); await this.insertColOption(context, column, colId, ncMeta);
// on column update, delete any optimised single query cache // on column update, delete any optimised single query cache

4
packages/nocodb/src/version-upgrader/NcUpgrader.ts

@ -12,7 +12,8 @@ import ncXcdbLTARUpgrader from './upgraders/0108002_ncXcdbLTARUpgrader';
import ncXcdbLTARIndexUpgrader from './upgraders/0111002_ncXcdbLTARIndexUpgrader'; import ncXcdbLTARIndexUpgrader from './upgraders/0111002_ncXcdbLTARIndexUpgrader';
import ncXcdbCreatedAndUpdatedSystemFieldsUpgrader from './upgraders/0111005_ncXcdbCreatedAndUpdatedSystemFieldsUpgrader'; import ncXcdbCreatedAndUpdatedSystemFieldsUpgrader from './upgraders/0111005_ncXcdbCreatedAndUpdatedSystemFieldsUpgrader';
import ncDatasourceDecrypt from './upgraders/0225002_ncDatasourceDecrypt'; import ncDatasourceDecrypt from './upgraders/0225002_ncDatasourceDecrypt';
// import dupColMergeUpgrader from './0205003_dupColMergeUpgrader'; // import ncBrokenLinkRecovery from './upgraders/0227002_ncBrokenLinkRecovery';
// import dupColMergeUpgrader from './upgraders/0227003_dupColMergeUpgrader';
import type { MetaService } from '~/meta/meta.service'; import type { MetaService } from '~/meta/meta.service';
import type { NcConfig } from '~/interface/config'; import type { NcConfig } from '~/interface/config';
import { T } from '~/utils'; import { T } from '~/utils';
@ -152,6 +153,7 @@ export default class NcUpgrader {
{ name: '0111005', handler: ncXcdbCreatedAndUpdatedSystemFieldsUpgrader }, { name: '0111005', handler: ncXcdbCreatedAndUpdatedSystemFieldsUpgrader },
{ name: '0225002', handler: ncDatasourceDecrypt }, { name: '0225002', handler: ncDatasourceDecrypt },
// disable for now // disable for now
// { name: '0257002', handler: ncBrokenLinkRecovery },
// { name: '0227000', handler: dupColMergeUpgrader }, // { name: '0227000', handler: dupColMergeUpgrader },
]; ];
} }

0
packages/nocodb/src/version-upgrader/0227000_dupColMergeUpgrader.ts → packages/nocodb/src/version-upgrader/upgraders/0227000_dupColMergeUpgrader.ts

297
packages/nocodb/src/version-upgrader/upgraders/0227002_ncBrokenLinkRecovery.ts

@ -0,0 +1,297 @@
import { RelationTypes, UITypes } from 'nocodb-sdk';
import type { NcUpgraderCtx } from '~/version-upgrader/NcUpgrader';
import type { MetaService } from '~/meta/meta.service';
import { MetaTable } from '~/utils/globals';
import { Column } from '~/models';
import { isEE } from '~/utils';
/**
* This upgrader look for any broken link and try to recover it
* using the existing data
*/
const logger = {
log: (message: string) => {
console.log(`[0227002_ncBrokenLinkRecovery ${Date.now()}] ` + message);
},
error: (message: string) => {
console.error(`[0227002_ncBrokenLinkRecovery ${Date.now()}] ` + message);
},
};
export default async function ({ ncMeta }: NcUpgraderCtx) {
// Get all broken link columns which doesn't have colOptions
const columns = await ncMeta
.knex(MetaTable.COLUMNS)
.select(`${MetaTable.COLUMNS}.*`)
.leftJoin(
MetaTable.COL_RELATIONS,
`${MetaTable.COLUMNS}.id`,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
)
.where(`${MetaTable.COLUMNS}.uidt`, UITypes.LinkToAnotherRecord)
.whereNull(`${MetaTable.COL_RELATIONS}.id`);
// Recover broken link
for (const column of columns) {
logger.log(`Recovering column '${column.title}' (ID: '${column.id}')`);
let relatedTableId;
// check any lookup or rollup column is using this column
const lookupColumns = await ncMeta
.knex(MetaTable.COL_LOOKUP)
.select(`${MetaTable.COL_LOOKUP}.*`)
.where(`${MetaTable.COL_LOOKUP}.fk_relation_column_id`, column.id);
for (const lookupColumn of lookupColumns) {
const lookupCol = await ncMeta
.knex(MetaTable.COLUMNS)
.select(`${MetaTable.COLUMNS}.fk_model_id`)
.where(`${MetaTable.COLUMNS}.id`, lookupColumn.fk_lookup_column_id)
.first();
if (lookupCol) {
relatedTableId = lookupCol.fk_model_id;
break;
}
}
if (!relatedTableId) {
const rollupColumns = await ncMeta
.knex(MetaTable.COL_ROLLUP)
.select(`${MetaTable.COL_ROLLUP}.*`)
.where(`${MetaTable.COL_ROLLUP}.fk_relation_column_id`, column.id);
for (const rollupColumn of rollupColumns) {
const rollupCol = await ncMeta
.knex(MetaTable.COLUMNS)
.select(`${MetaTable.COLUMNS}.fk_model_id`)
.where(`${MetaTable.COLUMNS}.id`, rollupColumn.fk_rollup_column_id)
.first();
if (rollupCol) {
relatedTableId = rollupCol.fk_model_id;
break;
}
}
}
// if related table is not found then iterate over all links which is related to current table
const linksQb = ncMeta
.knex(MetaTable.COL_RELATIONS)
.select(`${MetaTable.COL_RELATIONS}.*`)
.select(`${MetaTable.COLUMNS}.fk_model_id`)
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
column.fk_model_id,
)
.join(
MetaTable.COLUMNS,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
`${MetaTable.COLUMNS}.id`,
);
if (relatedTableId) {
linksQb.where(`${MetaTable.COLUMNS}.fk_model_id`, relatedTableId);
}
const links = await linksQb;
let foundAndMapped = false;
// iterate over all links which is related to current table and if found relation which doesn't have link in the related table then use it to populate colOptions
for (const link of links) {
const relatedTableId = link.fk_model_id;
let columnInCurrTable = null;
if (link.type === RelationTypes.HAS_MANY) {
// check for bt column in current table
columnInCurrTable = await ncMeta
.knex(MetaTable.COL_RELATIONS)
.join(
MetaTable.COLUMNS,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
`${MetaTable.COLUMNS}.id`,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
relatedTableId,
)
.where(`${MetaTable.COL_RELATIONS}.type`, RelationTypes.BELONGS_TO)
.where(
`${MetaTable.COL_RELATIONS}.fk_child_column_id`,
link.fk_child_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_parent_column_id`,
link.fk_parent_column_id,
)
.first();
} else if (link.type === RelationTypes.ONE_TO_ONE) {
// check for one to one column in current table and confirm type in meta
columnInCurrTable = await ncMeta
.knex(MetaTable.COL_RELATIONS)
.join(
MetaTable.COLUMNS,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
`${MetaTable.COLUMNS}.id`,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
relatedTableId,
)
.where(`${MetaTable.COL_RELATIONS}.type`, RelationTypes.ONE_TO_ONE)
.where(
`${MetaTable.COL_RELATIONS}.fk_child_column_id`,
link.fk_child_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_parent_column_id`,
link.fk_parent_column_id,
)
.first();
} else if (link.type === RelationTypes.BELONGS_TO) {
// check for hm column in current table
columnInCurrTable = await ncMeta
.knex(MetaTable.COL_RELATIONS)
.join(
MetaTable.COLUMNS,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
`${MetaTable.COLUMNS}.id`,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
relatedTableId,
)
.where(`${MetaTable.COL_RELATIONS}.type`, RelationTypes.HAS_MANY)
.where(
`${MetaTable.COL_RELATIONS}.fk_child_column_id`,
link.fk_child_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_parent_column_id`,
link.fk_parent_column_id,
)
.first();
} else if (link.type === RelationTypes.MANY_TO_MANY) {
// check for mtm column in current table
columnInCurrTable = await ncMeta
.knex(MetaTable.COL_RELATIONS)
.join(
MetaTable.COLUMNS,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
`${MetaTable.COLUMNS}.id`,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
relatedTableId,
)
.where(`${MetaTable.COL_RELATIONS}.type`, RelationTypes.BELONGS_TO)
.where(
`${MetaTable.COL_RELATIONS}.fk_child_column_id`,
link.fk_parent_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_parent_column_id`,
link.fk_parent_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_mm_model_id`,
link.fk_mm_model_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_mm_child_column_id`,
link.fk_mm_parent_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_mm_parent_column_id`,
link.fk_mm_child_column_id,
)
.first();
}
if (!columnInCurrTable) {
// generate meta and insert into colOptions
const commonProps: Record<string, unknown> = {
id: await (ncMeta as MetaService).genNanoid(MetaTable.COL_RELATIONS),
fk_column_id: column.id,
fk_related_model_id: relatedTableId,
created_at: link.created_at,
updated_at: link.updated_at,
virtual: link.virtual,
base_id: link.base_id,
};
if (isEE) {
commonProps.fk_workspace_id = link.fk_workspace_id;
}
// based on type insert data into colOptions
switch (link.type) {
case RelationTypes.HAS_MANY:
// insert data into colOptions
await ncMeta.knex(MetaTable.COL_RELATIONS).insert({
...commonProps,
type: RelationTypes.BELONGS_TO,
fk_child_column_id: link.fk_child_column_id,
fk_parent_column_id: link.fk_parent_column_id,
});
break;
case RelationTypes.ONE_TO_ONE:
{
// insert data into colOptions
await ncMeta.knex(MetaTable.COL_RELATIONS).insert({
...commonProps,
type: RelationTypes.ONE_TO_ONE,
fk_child_column_id: link.fk_child_column_id,
fk_parent_column_id: link.fk_parent_column_id,
});
}
break;
case RelationTypes.BELONGS_TO:
// insert data into colOptions
await ncMeta.knex(MetaTable.COL_RELATIONS).insert({
...commonProps,
type: RelationTypes.HAS_MANY,
fk_child_column_id: link.fk_child_column_id,
fk_parent_column_id: link.fk_parent_column_id,
});
break;
case RelationTypes.MANY_TO_MANY:
// insert data into colOptions
await ncMeta.knex(MetaTable.COL_RELATIONS).insert({
...commonProps,
type: RelationTypes.MANY_TO_MANY,
fk_child_column_id: link.fk_parent_column_id,
fk_parent_column_id: link.fk_child_column_id,
fk_mm_model_id: link.fk_mm_model_id,
fk_mm_child_column_id: link.fk_mm_parent_column_id,
fk_mm_parent_column_id: link.fk_mm_child_column_id,
});
break;
}
foundAndMapped = true;
break;
}
}
if (!foundAndMapped) {
logger.error(
`No related column found for link column '${column.title}' (ID: '${column.id}'). Deleting it.`,
);
// delete the link column since it's not useful anymore and not recoverable
await Column.delete(
{
workspace_id: column.workspace_id,
base_id: column.base_id,
},
column.id,
ncMeta,
);
} else {
logger.log(`Recovered column '${column.title}' (ID: '${column.id}')`);
}
}
}
Loading…
Cancel
Save