Browse Source

Merge pull request #5030 from nocodb/fix/4615-constraint_name_generation

fix: Populate unique fk constraint name
pull/5094/head
աɨռɢӄաօռɢ 2 years ago committed by GitHub
parent
commit
496af3b2f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      packages/nc-gui/assets/style.scss
  2. 31
      packages/nocodb/src/lib/db/sql-client/lib/KnexClient.ts
  3. 57
      packages/nocodb/src/lib/meta/api/columnApis.ts
  4. 4
      packages/nocodb/src/lib/meta/api/helpers/index.ts
  5. 8
      packages/nocodb/src/lib/meta/api/helpers/populateMeta.ts
  6. 5
      packages/nocodb/src/lib/meta/api/metaDiffApis.ts
  7. 9
      packages/nocodb/src/lib/models/Model.ts
  8. 71
      packages/nocodb/src/lib/models/View.ts
  9. 38
      packages/nocodb/src/lib/version-upgrader/ncStickyColumnUpgrader.ts

2
packages/nc-gui/assets/style.scss

@ -308,7 +308,7 @@ a {
.ant-btn-loading-icon{
& > span {
@apply block bg-red-500
@apply block;
}
}

31
packages/nocodb/src/lib/db/sql-client/lib/KnexClient.ts

@ -2367,9 +2367,9 @@ class KnexClient extends SqlClient {
const foreignKeyName = args.foreignKeyName || null;
try {
// s = await this.sqlClient.schema.index(Object.keys(args.columns));
await this.sqlClient.schema.table(args.childTable, function (table) {
const upQb = this.sqlClient.schema.table(
args.childTable,
function (table) {
table = table
.foreign(args.childColumn, foreignKeyName)
.references(args.parentColumn)
@ -2379,27 +2379,14 @@ class KnexClient extends SqlClient {
table = table.onUpdate(args.onUpdate);
}
if (args.onDelete) {
table = table.onDelete(args.onDelete);
table.onDelete(args.onDelete);
}
});
}
);
const upStatement =
this.querySeparator() +
(await this.sqlClient.schema
.table(args.childTable, function (table) {
table = table
.foreign(args.childColumn, foreignKeyName)
.references(args.parentColumn)
.on(args.parentTable);
await upQb;
if (args.onUpdate) {
table = table.onUpdate(args.onUpdate);
}
if (args.onDelete) {
table = table.onDelete(args.onDelete);
}
})
.toQuery());
const upStatement = this.querySeparator() + upQb.toQuery();
this.emit(`Success : ${upStatement}`);
@ -2407,7 +2394,7 @@ class KnexClient extends SqlClient {
this.querySeparator() +
this.sqlClient.schema
.table(args.childTable, function (table) {
table = table.dropForeign(args.childColumn, foreignKeyName);
table.dropForeign(args.childColumn, foreignKeyName);
})
.toQuery();

57
packages/nocodb/src/lib/meta/api/columnApis.ts

@ -50,12 +50,26 @@ export enum Altered {
UPDATE_COLUMN = 8,
}
// generate unique foreign key constraint name for foreign key
const generateFkName = (parent: TableType, child: TableType) => {
// generate a unique constraint name by taking first 10 chars of parent and child table name (by replacing all non word chars with _)
// and appending a random string of 15 chars maximum length.
// In database constraint name can be upto 64 chars and here we are generating a name of maximum 40 chars
const constraintName = `fk_${parent.table_name
.replace(/\W+/g, '_')
.slice(0, 10)}_${child.table_name
.replace(/\W+/g, '_')
.slice(0, 10)}_${randomID(15)}`;
return constraintName;
};
async function createHmAndBtColumn(
child: Model,
parent: Model,
childColumn: Column,
type?: RelationTypes,
alias?: string,
fkColName?: string,
virtual = false,
isSystemCol = false
) {
@ -79,6 +93,7 @@ async function createHmAndBtColumn(
fk_related_model_id: parent.id,
virtual,
system: isSystemCol,
fk_index_name: fkColName,
});
}
// save hm column
@ -97,6 +112,7 @@ async function createHmAndBtColumn(
fk_related_model_id: child.id,
virtual,
system: isSystemCol,
fk_index_name: fkColName,
});
}
}
@ -262,6 +278,7 @@ export async function columnAdd(
`${parent.table_name}_id`
);
let foreignKeyName;
{
// create foreign key
const newColumn = {
@ -307,6 +324,7 @@ export async function columnAdd(
// ignore relation creation if virtual
if (!(req.body as LinkToAnotherColumnReqType).virtual) {
foreignKeyName = generateFkName(parent, child);
// create relation
await sqlMgr.sqlOpPlus(base, 'relationCreate', {
childColumn: fkColName,
@ -316,6 +334,7 @@ export async function columnAdd(
onUpdate: 'NO ACTION',
type: 'real',
parentColumn: parent.primaryKey.column_name,
foreignKeyName,
});
}
@ -338,6 +357,7 @@ export async function columnAdd(
childColumn,
(req.body as LinkToAnotherColumnReqType).type as RelationTypes,
(req.body as LinkToAnotherColumnReqType).title,
foreignKeyName,
(req.body as LinkToAnotherColumnReqType).virtual
);
} else if ((req.body as LinkToAnotherColumnReqType).type === 'mm') {
@ -399,7 +419,13 @@ export async function columnAdd(
columns: associateTableCols,
});
let foreignKeyName1;
let foreignKeyName2;
if (!(req.body as LinkToAnotherColumnReqType).virtual) {
foreignKeyName1 = generateFkName(parent, child);
foreignKeyName2 = generateFkName(parent, child);
const rel1Args = {
...req.body,
childTable: aTn,
@ -407,6 +433,7 @@ export async function columnAdd(
parentTable: parent.table_name,
parentColumn: parentPK.column_name,
type: 'real',
foreignKeyName: foreignKeyName1,
};
const rel2Args = {
...req.body,
@ -415,6 +442,7 @@ export async function columnAdd(
parentTable: child.table_name,
parentColumn: childPK.column_name,
type: 'real',
foreignKeyName: foreignKeyName2,
};
await sqlMgr.sqlOpPlus(base, 'relationCreate', rel1Args);
@ -433,6 +461,7 @@ export async function columnAdd(
childCol,
null,
null,
foreignKeyName1,
(req.body as LinkToAnotherColumnReqType).virtual,
true
);
@ -442,6 +471,7 @@ export async function columnAdd(
parentCol,
null,
null,
foreignKeyName2,
(req.body as LinkToAnotherColumnReqType).virtual,
true
);
@ -1724,6 +1754,31 @@ const deleteHmOrBtRelation = async (
},
ignoreFkDelete = false
) => {
let foreignKeyName;
// if relationColOpt is not provided, extract it from child table
// and get the foreign key name for dropping the foreign key
if (!relationColOpt) {
foreignKeyName = (
(
await childTable.getColumns().then((cols) => {
return cols?.find((c) => {
return (
c.uidt === UITypes.LinkToAnotherRecord &&
c.colOptions.fk_related_model_id === parentTable.id &&
(c.colOptions as LinkToAnotherRecordType).fk_child_column_id ===
childColumn.id &&
(c.colOptions as LinkToAnotherRecordType).fk_parent_column_id ===
parentColumn.id
);
});
})
).colOptions as LinkToAnotherRecordType
).fk_index_name;
} else {
foreignKeyName = relationColOpt.fk_index_name;
}
// todo: handle relation delete exception
try {
await sqlMgr.sqlOpPlus(base, 'relationDelete', {
@ -1731,7 +1786,7 @@ const deleteHmOrBtRelation = async (
childTable: childTable.table_name,
parentTable: parentTable.table_name,
parentColumn: parentColumn.column_name,
// foreignKeyName: relation.fkn
foreignKeyName,
});
} catch (e) {
console.log(e);

4
packages/nocodb/src/lib/meta/api/helpers/index.ts

@ -1,3 +1,3 @@
import { populateMeta } from "./populateMeta";
import { populateMeta } from './populateMeta';
export { populateMeta }
export { populateMeta };

8
packages/nocodb/src/lib/meta/api/helpers/populateMeta.ts

@ -5,7 +5,9 @@ import NcHelp from '../../../utils/NcHelp';
import Base from '../../../models/Base';
import View from '../../../models/View';
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2';
import getTableNameAlias, { getColumnNameAlias } from '../../helpers/getTableName';
import getTableNameAlias, {
getColumnNameAlias,
} from '../../helpers/getTableName';
import LinkToAnotherRecordColumn from '../../../models/LinkToAnotherRecordColumn';
import getColumnUiType from '../../helpers/getColumnUiType';
import mapDefaultPrimaryValue from '../../helpers/mapDefaultPrimaryValue';
@ -179,7 +181,7 @@ export async function populateMeta(base: Base, project: Project): Promise<any> {
// column_id,
fk_child_column_id: rel_column_id,
fk_parent_column_id: ref_rel_column_id,
fk_index_name: rel.fkn,
fk_index_name: rel.cstn,
ur: rel.ur,
dr: rel.dr,
order: colOrder++,
@ -259,7 +261,7 @@ export async function populateMeta(base: Base, project: Project): Promise<any> {
const models = await Model.list({ project_id: project.id, base_id: base.id });
for (const model of models) {
const views = await model.getViews()
const views = await model.getViews();
for (const view of views) {
if (view.type === ViewTypes.GRID) {
await View.fixPVColumnForView(view.id);

5
packages/nocodb/src/lib/meta/api/metaDiffApis.ts

@ -107,6 +107,7 @@ type MetaDiffChange = {
cn?: string;
rcn?: string;
relationType: RelationTypes;
cstn?: string;
}
);
@ -146,6 +147,7 @@ async function getMetaDiff(
cn: string;
rcn: string;
found?: any;
cstn?: string;
}> = (await sqlClient.relationListAll())?.data?.list;
for (const table of tableList) {
@ -394,6 +396,7 @@ async function getMetaDiff(
rcn: relation.rcn,
msg: `New relation added`,
relationType: RelationTypes.BELONGS_TO,
cstn: relation.cstn,
});
}
if (!relation?.found?.[RelationTypes.HAS_MANY]) {
@ -736,6 +739,7 @@ export async function metaDiffSync(req, res) {
fk_parent_column_id: parentCol.id,
fk_child_column_id: childCol.id,
virtual: false,
fk_index_name: change.cstn,
});
} else if (change.relationType === RelationTypes.HAS_MANY) {
const title = getUniqueColumnAliasName(
@ -751,6 +755,7 @@ export async function metaDiffSync(req, res) {
fk_parent_column_id: parentCol.id,
fk_child_column_id: childCol.id,
virtual: false,
fk_index_name: change.cstn,
});
}
});

9
packages/nocodb/src/lib/models/Model.ts

@ -606,11 +606,16 @@ export default class Model implements TableType {
newPvCol.id
);
const grid_views_with_column = await ncMeta.metaList2(null, null, MetaTable.GRID_VIEW_COLUMNS, {
const grid_views_with_column = await ncMeta.metaList2(
null,
null,
MetaTable.GRID_VIEW_COLUMNS,
{
condition: {
fk_column_id: newPvCol.id,
},
}
})
);
if (grid_views_with_column.length) {
for (const gv of grid_views_with_column) {

71
packages/nocodb/src/lib/models/View.ts

@ -633,15 +633,25 @@ export default class View implements ViewType {
// keep primary_value_column always visible and first in grid view
if (view.type === ViewTypes.GRID) {
const primary_value_column_meta = await ncMeta.metaGet2(null, null, MetaTable.COLUMNS, {
const primary_value_column_meta = await ncMeta.metaGet2(
null,
null,
MetaTable.COLUMNS,
{
fk_model_id: view.fk_model_id,
pv: true,
});
}
);
const primary_value_column = await ncMeta.metaGet2(null, null, MetaTable.GRID_VIEW_COLUMNS, {
const primary_value_column = await ncMeta.metaGet2(
null,
null,
MetaTable.GRID_VIEW_COLUMNS,
{
fk_view_id: view.id,
fk_column_id: primary_value_column_meta.id,
});
}
);
if (primary_value_column && primary_value_column.id === colId) {
updateObj.order = 1;
@ -1108,14 +1118,19 @@ export default class View implements ViewType {
const scope = this.extractViewColumnsTableNameScope(view);
if (view.type === ViewTypes.GRID) {
const primary_value_column = await ncMeta.metaGet2(null, null, MetaTable.COLUMNS, {
const primary_value_column = await ncMeta.metaGet2(
null,
null,
MetaTable.COLUMNS,
{
fk_model_id: view.fk_model_id,
pv: true,
})
}
);
// keep primary_value_column always visible
if (primary_value_column) {
ignoreColdIds.push(primary_value_column.id)
ignoreColdIds.push(primary_value_column.id);
}
}
@ -1179,27 +1194,41 @@ export default class View implements ViewType {
static async fixPVColumnForView(viewId, ncMeta = Noco.ncMeta) {
// get a list of view columns sorted by order
const view_columns = await ncMeta.metaList2(null, null, MetaTable.GRID_VIEW_COLUMNS, {
const view_columns = await ncMeta.metaList2(
null,
null,
MetaTable.GRID_VIEW_COLUMNS,
{
condition: {
fk_view_id: viewId,
},
orderBy: {
order: 'asc',
},
});
const view_columns_meta = []
}
);
const view_columns_meta = [];
// get column meta for each view column
for (const col of view_columns) {
const col_meta = await ncMeta.metaGet2(null, null, MetaTable.COLUMNS, col.fk_column_id);
const col_meta = await ncMeta.metaGet2(
null,
null,
MetaTable.COLUMNS,
col.fk_column_id
);
view_columns_meta.push(col_meta);
}
const primary_value_column_meta = view_columns_meta.find((col) => col.pv);
if (primary_value_column_meta) {
const primary_value_column = view_columns.find((col) => col.fk_column_id === primary_value_column_meta.id);
const primary_value_column_index = view_columns.findIndex((col) => col.fk_column_id === primary_value_column_meta.id);
const primary_value_column = view_columns.find(
(col) => col.fk_column_id === primary_value_column_meta.id
);
const primary_value_column_index = view_columns.findIndex(
(col) => col.fk_column_id === primary_value_column_meta.id
);
const view_orders = view_columns.map((col) => col.order);
const view_min_order = Math.min(...view_orders);
@ -1210,7 +1239,7 @@ export default class View implements ViewType {
null,
MetaTable.GRID_VIEW_COLUMNS,
{ show: true },
primary_value_column.id,
primary_value_column.id
);
await NocoCache.set(
`${CacheScope.GRID_VIEW_COLUMN}:${primary_value_column.id}`,
@ -1218,7 +1247,10 @@ export default class View implements ViewType {
);
}
if (primary_value_column.order === view_min_order && view_orders.filter((o) => o === view_min_order).length === 1) {
if (
primary_value_column.order === view_min_order &&
view_orders.filter((o) => o === view_min_order).length === 1
) {
// if primary_value_column is in first order do nothing
return;
} else {
@ -1245,14 +1277,19 @@ export default class View implements ViewType {
}
}
const views = await ncMeta.metaList2(null, null, MetaTable.GRID_VIEW_COLUMNS, {
const views = await ncMeta.metaList2(
null,
null,
MetaTable.GRID_VIEW_COLUMNS,
{
condition: {
fk_view_id: viewId,
},
orderBy: {
order: 'asc',
},
});
}
);
await NocoCache.setList(CacheScope.GRID_VIEW_COLUMN, [viewId], views);
}
}

38
packages/nocodb/src/lib/version-upgrader/ncStickyColumnUpgrader.ts

@ -6,32 +6,47 @@ import { MetaTable } from '../utils/globals';
// this upgrader will make primary value column first column in grid views
export default async function ({ ncMeta }: NcUpgraderCtx) {
const grid_columns = await ncMeta.metaList2(null, null, MetaTable.GRID_VIEW_COLUMNS);
const grid_views = [...new Set(grid_columns.map((col) => col.fk_view_id))]
const grid_columns = await ncMeta.metaList2(
null,
null,
MetaTable.GRID_VIEW_COLUMNS
);
const grid_views = [...new Set(grid_columns.map((col) => col.fk_view_id))];
for (const view_id of grid_views) {
// get a list of view columns sorted by order
const view_columns = await ncMeta.metaList2(null, null, MetaTable.GRID_VIEW_COLUMNS, {
const view_columns = await ncMeta.metaList2(
null,
null,
MetaTable.GRID_VIEW_COLUMNS,
{
condition: {
fk_view_id: view_id,
},
orderBy: {
order: 'asc',
},
});
const view_columns_meta = []
}
);
const view_columns_meta = [];
// get column meta for each view column
for (const col of view_columns) {
const col_meta = await ncMeta.metaGet(null, null, MetaTable.COLUMNS, { id: col.fk_column_id });
const col_meta = await ncMeta.metaGet(null, null, MetaTable.COLUMNS, {
id: col.fk_column_id,
});
view_columns_meta.push(col_meta);
}
const primary_value_column_meta = view_columns_meta.find((col) => col.pv);
if (primary_value_column_meta) {
const primary_value_column = view_columns.find((col) => col.fk_column_id === primary_value_column_meta.id);
const primary_value_column_index = view_columns.findIndex((col) => col.fk_column_id === primary_value_column_meta.id);
const primary_value_column = view_columns.find(
(col) => col.fk_column_id === primary_value_column_meta.id
);
const primary_value_column_index = view_columns.findIndex(
(col) => col.fk_column_id === primary_value_column_meta.id
);
const view_orders = view_columns.map((col) => col.order);
const view_min_order = Math.min(...view_orders);
@ -42,11 +57,14 @@ export default async function ({ ncMeta }: NcUpgraderCtx) {
null,
MetaTable.GRID_VIEW_COLUMNS,
{ show: true },
primary_value_column.id,
primary_value_column.id
);
}
if (primary_value_column.order === view_min_order && view_orders.filter((o) => o === view_min_order).length === 1) {
if (
primary_value_column.order === view_min_order &&
view_orders.filter((o) => o === view_min_order).length === 1
) {
// if primary_value_column is in first order do nothing
continue;
} else {

Loading…
Cancel
Save