diff --git a/packages/nc-gui/components/template/Editor.vue b/packages/nc-gui/components/template/Editor.vue index 6d1092bb5e..3d44a13a73 100644 --- a/packages/nc-gui/components/template/Editor.vue +++ b/packages/nc-gui/components/template/Editor.vue @@ -12,6 +12,7 @@ import { computed, createEventHook, extractSdkResponseErrorMsg, + fieldLengthValidator, fieldRequiredValidator, getDateFormat, getDateTimeFormat, @@ -110,12 +111,15 @@ const data = reactive<{ }) const validators = computed(() => - data.tables.reduce]>>((acc, table, tableIdx) => { + data.tables.reduce]>>((acc: Record, table, tableIdx) => { acc[`tables.${tableIdx}.table_name`] = [fieldRequiredValidator()] hasSelectColumn.value[tableIdx] = false table.columns?.forEach((column, columnIdx) => { - acc[`tables.${tableIdx}.columns.${columnIdx}.column_name`] = [fieldRequiredValidator()] + acc[`tables.${tableIdx}.columns.${columnIdx}.column_name`] = [ + fieldRequiredValidator(), + fieldLengthValidator(project.value?.bases?.[0].type || ClientType.MYSQL), + ] acc[`tables.${tableIdx}.columns.${columnIdx}.uidt`] = [fieldRequiredValidator()] if (isSelect(column)) { hasSelectColumn.value[tableIdx] = true diff --git a/packages/nc-gui/composables/useColumnCreateStore.ts b/packages/nc-gui/composables/useColumnCreateStore.ts index 4a23f1e013..d1b5ebb68b 100644 --- a/packages/nc-gui/composables/useColumnCreateStore.ts +++ b/packages/nc-gui/composables/useColumnCreateStore.ts @@ -27,7 +27,8 @@ interface ValidationsObj { const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState( (meta: Ref, column: Ref) => { - const { sqlUis, isMysql: isMysqlFunc, isPg: isPgFunc, isMssql: isMssqlFunc } = useProject() + const { project, sqlUis, isMysql: isMysqlFunc, isPg: isPgFunc, isMssql: isMssqlFunc } = useProject() + const { $api } = useNuxtApp() const { getMeta } = useMetas() @@ -93,6 +94,7 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState }) }, }, + fieldLengthValidator(project.value?.bases?.[0].type || ClientType.MYSQL), ], uidt: [ { diff --git a/packages/nc-gui/lang/en.json b/packages/nc-gui/lang/en.json index 7169e07026..bdbc84d36d 100644 --- a/packages/nc-gui/lang/en.json +++ b/packages/nc-gui/lang/en.json @@ -690,6 +690,7 @@ "nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _", "followingCharactersAreNotAllowed": "Following characters are not allowed", "columnNameRequired": "Column name is required", + "columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters", "projectNameExceeds50Characters": "Project name exceeds 50 characters", "projectNameCannotStartWithSpace": "Project name cannot start with space", "requiredField": "Required field", diff --git a/packages/nc-gui/utils/validation.ts b/packages/nc-gui/utils/validation.ts index 3524a1f928..fb1c50ea41 100644 --- a/packages/nc-gui/utils/validation.ts +++ b/packages/nc-gui/utils/validation.ts @@ -80,6 +80,32 @@ export const fieldRequiredValidator = () => { } } +export const fieldLengthValidator = (sqlClientType: string) => { + return { + validator: (rule: any, value: any) => { + const { t } = getI18n().global + + // no limit for sqlite but set as 255 + let fieldLengthLimit = 255 + + if (sqlClientType === 'mysql2' || sqlClientType === 'mysql') { + fieldLengthLimit = 64 + } else if (sqlClientType === 'pg') { + fieldLengthLimit = 59 + } else if (sqlClientType === 'mssql') { + fieldLengthLimit = 128 + } + + return new Promise((resolve, reject) => { + if (value?.length > fieldLengthLimit) { + reject(new Error(t('msg.error.columnNameExceedsCharacters', { value: fieldLengthLimit }))) + } + resolve(true) + }) + }, + } +} + export const importUrlValidator = { validator: (rule: any, value: any) => { return new Promise((resolve, reject) => { diff --git a/packages/nocodb/src/lib/meta/api/columnApis.ts b/packages/nocodb/src/lib/meta/api/columnApis.ts index 97fbb68c06..ab36b0103c 100644 --- a/packages/nocodb/src/lib/meta/api/columnApis.ts +++ b/packages/nocodb/src/lib/meta/api/columnApis.ts @@ -64,9 +64,27 @@ export async function columnAdd( const table = await Model.getWithInfo({ id: req.params.tableId, }); + const base = await Base.get(table.base_id); + const project = await base.getProject(); + if (req.body.title || req.body.column_name) { + const dbDriver = NcConnectionMgrv2.get(base); + + const sqlClientType = dbDriver.clientType(); + + const mxColumnLength = Column.getMaxColumnNameLength(sqlClientType); + + if ((req.body.title || req.body.column_name).length > mxColumnLength) { + NcError.badRequest( + `Column name ${ + req.body.title || req.body.column_name + } exceeds ${mxColumnLength} characters` + ); + } + } + if ( !isVirtualCol(req.body) && !(await Column.checkTitleAvailable({ @@ -638,8 +656,21 @@ export async function columnUpdate(req: Request, res: Response) { const table = await Model.getWithInfo({ id: column.fk_model_id, }); + const base = await Base.get(table.base_id); + const sqlClient = await NcConnectionMgrv2.getSqlClient(base); + + const sqlClientType = sqlClient.knex.clientType(); + + const mxColumnLength = Column.getMaxColumnNameLength(sqlClientType); + + if (req.body.column_name.length > mxColumnLength) { + NcError.badRequest( + `Column name ${req.body.column_name} exceeds ${mxColumnLength} characters` + ); + } + if ( !isVirtualCol(req.body) && !(await Column.checkTitleAvailable({ diff --git a/packages/nocodb/src/lib/meta/api/tableApis.ts b/packages/nocodb/src/lib/meta/api/tableApis.ts index 8eb3d6a10e..b47fa5e5e6 100644 --- a/packages/nocodb/src/lib/meta/api/tableApis.ts +++ b/packages/nocodb/src/lib/meta/api/tableApis.ts @@ -148,10 +148,11 @@ export async function tableCreate(req: Request, res) { } const sqlMgr = await ProjectMgrv2.getSqlMgr(project); + const sqlClient = await NcConnectionMgrv2.getSqlClient(base); let tableNameLengthLimit = 255; - const sqlClientType = sqlClient.clientType; + const sqlClientType = sqlClient.knex.clientType(); if (sqlClientType === 'mysql2' || sqlClientType === 'mysql') { tableNameLengthLimit = 64; } else if (sqlClientType === 'pg') { @@ -164,6 +165,16 @@ export async function tableCreate(req: Request, res) { NcError.badRequest(`Table name exceeds ${tableNameLengthLimit} characters`); } + const mxColumnLength = Column.getMaxColumnNameLength(sqlClientType); + + for (const column of req.body.columns) { + if (column.column_name.length > mxColumnLength) { + NcError.badRequest( + `Column name ${column.column_name} exceeds ${mxColumnLength} characters` + ); + } + } + req.body.columns = req.body.columns?.map((c) => ({ ...getColumnPropsFromUIDT(c as any, base), cn: c.column_name, @@ -298,7 +309,7 @@ export async function tableUpdate(req: Request, res) { const sqlClient = await NcConnectionMgrv2.getSqlClient(base); let tableNameLengthLimit = 255; - const sqlClientType = sqlClient.clientType; + const sqlClientType = sqlClient.knex.clientType(); if (sqlClientType === 'mysql2' || sqlClientType === 'mysql') { tableNameLengthLimit = 64; } else if (sqlClientType === 'pg') { diff --git a/packages/nocodb/src/lib/models/Column.ts b/packages/nocodb/src/lib/models/Column.ts index 69b20dfe9a..6d7b2d5345 100644 --- a/packages/nocodb/src/lib/models/Column.ts +++ b/packages/nocodb/src/lib/models/Column.ts @@ -1151,4 +1151,17 @@ export default class Column implements ColumnType { colId ); } + + static getMaxColumnNameLength(sqlClientType: string) { + // no limit for sqlite but set as 255 + let fieldLengthLimit = 255; + if (sqlClientType === 'mysql2' || sqlClientType === 'mysql') { + fieldLengthLimit = 64; + } else if (sqlClientType === 'pg') { + fieldLengthLimit = 59; + } else if (sqlClientType === 'mssql') { + fieldLengthLimit = 128; + } + return fieldLengthLimit; + } }