From 1ae4e87297f1dc6e03e8a17f7ef062ad667f9f56 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:17 +0000 Subject: [PATCH 001/103] refactor: bring back CreateTime and LastModifiedTime uidt --- packages/nc-gui/utils/columnUtils.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/nc-gui/utils/columnUtils.ts b/packages/nc-gui/utils/columnUtils.ts index 7beafc22c1..0f42cb8762 100644 --- a/packages/nc-gui/utils/columnUtils.ts +++ b/packages/nc-gui/utils/columnUtils.ts @@ -138,6 +138,14 @@ const uiTypes = [ name: UITypes.User, icon: iconMap.account, }, + { + name: UITypes.CreateTime, + icon: iconMap.clock, + }, + { + name: UITypes.LastModifiedTime, + icon: iconMap.clock, + }, ] const getUIDTIcon = (uidt: UITypes | string) => { From 7c0097f245b96d0221604121dd33c1c684c62793 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:17 +0000 Subject: [PATCH 002/103] feat: created_at and updated_at column creation - WIP --- .../nocodb/src/services/columns.service.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index 515f25056e..22e3b71c38 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -1673,6 +1673,44 @@ export class ColumnsService { fk_model_id: table.id, }); + break; + case UITypes.CreateTime: + case UITypes.LastModifiedTime: + { + // check if column already exists, then just create a new column in meta + // else create a new column in meta and db + const existingColumn = await table.getColumns().then((col) => { + return col.find((c) => c.uidt === colBody.uidt); + }); + + if (!existingColumn) { + const sqlClient = await reuseOrSave('sqlClient', reuse, async () => + NcConnectionMgrv2.getSqlClient(source), + ); + const dbColumns = ( + await sqlClient.columnList({ + tn: table.table_name, + schema: source.getConfig()?.schema, + }) + )?.data?.list; + + // todo: check type as well + const dbColumn = dbColumns.find( + (c) => + c.column_name === + (c.uidt === UITypes.CreateTime ? 'created_at' : 'updated_at'), + ); + + if(!dbColumn){ + // create column in db + } + + await Column.insert({ + ...colBody, + fk_model_id: table.id, + }); + } + } break; default: { From 750756fdefb0e6460d8a83633f3e9b6d4d9dd798 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:18 +0000 Subject: [PATCH 003/103] feat: when deleting `CreateTime` or `LastModifiedTime`, keep the column and just delete the meta entry --- packages/nocodb/src/services/columns.service.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index 22e3b71c38..cf6af2e3f8 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -1701,8 +1701,8 @@ export class ColumnsService { (c.uidt === UITypes.CreateTime ? 'created_at' : 'updated_at'), ); - if(!dbColumn){ - // create column in db + if (!dbColumn) { + // create column in db } await Column.insert({ @@ -2148,6 +2148,13 @@ export class ColumnsService { } /* falls through to default */ } + + // on delete create time or last modified time, keep the column in table and delete the column from meta + case UITypes.CreateTime: + case UITypes.LastModifiedTime: { + await Column.delete(param.columnId, ncMeta); + } + break; default: { const tableUpdateBody = { ...table, From 674a402b37b25408c9e47a4249f828bc27d137eb Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:18 +0000 Subject: [PATCH 004/103] feat: `CreateTime` or `LastModifiedTime` - update column - WIP --- .../nocodb/src/services/columns.service.ts | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index cf6af2e3f8..6264829b1f 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -292,6 +292,41 @@ export class ColumnsService { NcError.notImplemented( `Updating ${colBody.uidt} => ${colBody.uidt} is not implemented`, ); + } else if ( + [UITypes.CreateTime, UITypes.LastModifiedTime].includes(colBody.uidt) + ) { + // todo: correct this + const existingColumn = await table.getColumns().then((col) => { + return col.find((c) => c.uidt === colBody.uidt); + }); + + if (!existingColumn) { + const sqlClient = await reuseOrSave('sqlClient', reuse, async () => + NcConnectionMgrv2.getSqlClient(source), + ); + const dbColumns = ( + await sqlClient.columnList({ + tn: table.table_name, + schema: source.getConfig()?.schema, + }) + )?.data?.list; + + // todo: check type as well + const dbColumn = dbColumns.find( + (c) => + c.column_name === + (c.uidt === UITypes.CreateTime ? 'created_at' : 'updated_at'), + ); + + if (!dbColumn) { + // create column in db + } + + await Column.insert({ + ...colBody, + fk_model_id: table.id, + }); + } } else if ( [UITypes.SingleSelect, UITypes.MultiSelect].includes(colBody.uidt) ) { @@ -2151,10 +2186,11 @@ export class ColumnsService { // on delete create time or last modified time, keep the column in table and delete the column from meta case UITypes.CreateTime: - case UITypes.LastModifiedTime: { - await Column.delete(param.columnId, ncMeta); - } - break; + case UITypes.LastModifiedTime: + { + await Column.delete(param.columnId, ncMeta); + } + break; default: { const tableUpdateBody = { ...table, From 68b0bea705f7e115abcc14a1c46647ab45bddabc Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:18 +0000 Subject: [PATCH 005/103] feat: `CreateTime` or `LastModifiedTime` - select query --- packages/nocodb/src/db/BaseModelSqlv2.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index 09878264b1..dce64eb5dd 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -101,6 +101,17 @@ function checkColumnRequired( return !fields || fields.includes(column.title); } +export function getColumnName(column: Column) { + switch (column.uidt) { + case UITypes.CreateTime: + return 'created_at'; + case UITypes.LastModifiedTime: + return 'updated_at'; + default: + return column.column_name; + } +} + /** * Base class for models * @@ -2113,7 +2124,10 @@ class BaseModelSqlv2 { if (!checkColumnRequired(column, fields, extractPkAndPv)) continue; switch (column.uidt) { - case UITypes.DateTime: + case UITypes.CreateTime: + case UITypes.LastModifiedTime: + case UITypes.DateTime: { + const columnName = getColumnName(column) ; if (this.isMySQL) { // MySQL stores timestamp in UTC but display in timezone // To verify the timezone, run `SELECT @@global.time_zone, @@session.time_zone;` @@ -2160,6 +2174,7 @@ class BaseModelSqlv2 { res[sanitize(column.id || column.column_name)] = sanitize( `${alias || this.tnPath}.${column.column_name}`, ); + } break; case UITypes.LinkToAnotherRecord: case UITypes.Lookup: From d000252ac1e9998447f7f541a9180136ae1576a6 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:18 +0000 Subject: [PATCH 006/103] feat: `CreateTime` or `LastModifiedTime` - select query - singleQuery --- packages/nocodb/src/db/BaseModelSqlv2.ts | 88 ++++++++++++------------ 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index dce64eb5dd..f43e14e51a 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -2126,55 +2126,55 @@ class BaseModelSqlv2 { switch (column.uidt) { case UITypes.CreateTime: case UITypes.LastModifiedTime: - case UITypes.DateTime: { - const columnName = getColumnName(column) ; - if (this.isMySQL) { - // MySQL stores timestamp in UTC but display in timezone - // To verify the timezone, run `SELECT @@global.time_zone, @@session.time_zone;` - // If it's SYSTEM, then the timezone is read from the configuration file - // if a timezone is set in a DB, the retrieved value would be converted to the corresponding timezone - // for example, let's say the global timezone is +08:00 in DB - // the value 2023-01-01 10:00:00 (UTC) would display as 2023-01-01 18:00:00 (UTC+8) - // our existing logic is based on UTC, during the query, we need to take the UTC value - // hence, we use CONVERT_TZ to convert back to UTC value - res[sanitize(column.id || column.column_name)] = this.dbDriver.raw( - `CONVERT_TZ(??, @@GLOBAL.time_zone, '+00:00')`, - [`${sanitize(alias || this.tnPath)}.${column.column_name}`], - ); - break; - } else if (this.isPg) { - // if there is no timezone info, - // convert to database timezone, - // then convert to UTC - if ( - column.dt !== 'timestamp with time zone' && - column.dt !== 'timestamptz' - ) { - res[sanitize(column.id || column.column_name)] = this.dbDriver - .raw( - `?? AT TIME ZONE CURRENT_SETTING('timezone') AT TIME ZONE 'UTC'`, - [`${sanitize(alias || this.tnPath)}.${column.column_name}`], - ) - .wrap('(', ')'); + case UITypes.DateTime: + { + const columnName = getColumnName(column); + if (this.isMySQL) { + // MySQL stores timestamp in UTC but display in timezone + // To verify the timezone, run `SELECT @@global.time_zone, @@session.time_zone;` + // If it's SYSTEM, then the timezone is read from the configuration file + // if a timezone is set in a DB, the retrieved value would be converted to the corresponding timezone + // for example, let's say the global timezone is +08:00 in DB + // the value 2023-01-01 10:00:00 (UTC) would display as 2023-01-01 18:00:00 (UTC+8) + // our existing logic is based on UTC, during the query, we need to take the UTC value + // hence, we use CONVERT_TZ to convert back to UTC value + res[sanitize(column.id || columnName)] = this.dbDriver.raw( + `CONVERT_TZ(??, @@GLOBAL.time_zone, '+00:00')`, + [`${sanitize(alias || this.tnPath)}.${columnName}`], + ); break; - } - } else if (this.isMssql) { - // if there is no timezone info, - // convert to database timezone, - // then convert to UTC - if (column.dt !== 'datetimeoffset') { - res[sanitize(column.id || column.column_name)] = - this.dbDriver.raw( + } else if (this.isPg) { + // if there is no timezone info, + // convert to database timezone, + // then convert to UTC + if ( + column.dt !== 'timestamp with time zone' && + column.dt !== 'timestamptz' + ) { + res[sanitize(column.id || columnName)] = this.dbDriver + .raw( + `?? AT TIME ZONE CURRENT_SETTING('timezone') AT TIME ZONE 'UTC'`, + [`${sanitize(alias || this.tnPath)}.${columnName}`], + ) + .wrap('(', ')'); + break; + } + } else if (this.isMssql) { + // if there is no timezone info, + // convert to database timezone, + // then convert to UTC + if (column.dt !== 'datetimeoffset') { + res[sanitize(column.id || columnName)] = this.dbDriver.raw( `CONVERT(DATETIMEOFFSET, ?? AT TIME ZONE 'UTC')`, - [`${sanitize(alias || this.tnPath)}.${column.column_name}`], + [`${sanitize(alias || this.tnPath)}.${columnName}`], ); - break; + break; + } } + res[sanitize(column.id || columnName)] = sanitize( + `${alias || this.tnPath}.${columnName}`, + ); } - res[sanitize(column.id || column.column_name)] = sanitize( - `${alias || this.tnPath}.${column.column_name}`, - ); - } break; case UITypes.LinkToAnotherRecord: case UITypes.Lookup: From ae5ccfece9c323f3f86460c04561233f0909d660 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:18 +0000 Subject: [PATCH 007/103] feat: `CreateTime` or `LastModifiedTime` - update behaviour --- packages/nocodb-sdk/src/lib/UITypes.ts | 12 ++++++++++++ .../nocodb/src/services/columns.service.ts | 19 +++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/nocodb-sdk/src/lib/UITypes.ts b/packages/nocodb-sdk/src/lib/UITypes.ts index 80f45d87ae..f9bffcb6b6 100644 --- a/packages/nocodb-sdk/src/lib/UITypes.ts +++ b/packages/nocodb-sdk/src/lib/UITypes.ts @@ -86,6 +86,18 @@ export function isVirtualCol( // UITypes.Count, ].includes((typeof col === 'object' ? col?.uidt : col)); } +export function isCreatedTimeOrUpdatedTimeCol( + col: + | UITypes + | { readonly uidt: UITypes | string } + | ColumnReqType + | ColumnType +) { + return [ + UITypes.CreateTime, + UITypes.LastModifiedTime, + ].includes((typeof col === 'object' ? col?.uidt : col)); +} export function isLinksOrLTAR( colOrUidt: ColumnType | { uidt: UITypes | string } | UITypes | string diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index 6264829b1f..0548d25c18 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -1,4 +1,11 @@ import { Injectable } from '@nestjs/common'; +import type { + ColumnReqType, + LinkToAnotherColumnReqType, + LinkToAnotherRecordType, + RelationTypes, + UserType, +} from 'nocodb-sdk'; import { AppEvents, isLinksOrLTAR, @@ -11,7 +18,7 @@ import { import { pluralize, singularize } from 'inflection'; import hash from 'object-hash'; import type SqlMgrv2 from '~/db/sql-mgr/v2/SqlMgrv2'; -import type { Base, LinkToAnotherRecordColumn } from '~/models'; +import type {Base, LinkToAnotherRecordColumn} from '~/models'; import type { ColumnReqType, LinkToAnotherColumnReqType, @@ -56,6 +63,7 @@ import Noco from '~/Noco'; import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2'; import { MetaTable } from '~/utils/globals'; import { MetaService } from '~/meta/meta.service'; +import {isCreatedTimeOrUpdatedTimeCol} from "nocodb-sdk/build/main/lib/UITypes"; // todo: move export enum Altered { @@ -171,6 +179,7 @@ export class ColumnsService { if ( !isVirtualCol(param.column) && + !isCreatedTimeOrUpdatedTimeCol(param.column) && !(await Column.checkTitleAvailable({ column_name: param.column.column_name, fk_model_id: column.fk_model_id, @@ -195,6 +204,7 @@ export class ColumnsService { parsed_tree?: any; }; if ( + isCreatedTimeOrUpdatedTimeCol(column) || [ UITypes.Lookup, UITypes.Rollup, @@ -295,9 +305,10 @@ export class ColumnsService { } else if ( [UITypes.CreateTime, UITypes.LastModifiedTime].includes(colBody.uidt) ) { - // todo: correct this - const existingColumn = await table.getColumns().then((col) => { - return col.find((c) => c.uidt === colBody.uidt); + // allow updating of title only + await Column.update(param.columnId, { + ...column, + title: colBody.title, }); if (!existingColumn) { From 626f96f3ca3dba5906479c24c40a098416d87b24 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:18 +0000 Subject: [PATCH 008/103] feat: add default value for created/modified time uidt --- packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts | 2 ++ packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts | 6 ++++-- packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts | 5 +++-- packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts | 2 ++ packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts | 2 ++ packages/nocodb/src/services/columns.service.ts | 2 ++ 6 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts index 2600945349..02f4e96acf 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts @@ -735,9 +735,11 @@ export class MssqlUi { break; case 'CreateTime': colProp.dt = 'datetime'; + colProp.cdf = 'GETDATE()'; break; case 'LastModifiedTime': colProp.dt = 'datetime'; + colProp.cdf = 'GETDATE()'; break; case 'AutoNumber': colProp.dt = 'int'; diff --git a/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts index 1fcf85e641..1b8cc0c8eb 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts @@ -1072,10 +1072,12 @@ export class MysqlUi { colProp.dt = 'datetime'; break; case 'CreateTime': - colProp.dt = 'datetime'; + colProp.dt = 'timestamp'; + colProp.cdf = 'CURRENT_TIMESTAMP' break; case 'LastModifiedTime': - colProp.dt = 'datetime'; + colProp.dt = 'timestamp'; + colProp.cdf = 'CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP' break; case 'AutoNumber': colProp.dt = 'int'; diff --git a/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts index c5ff48db62..f9df9bdc28 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts @@ -1159,7 +1159,7 @@ export class PgUi { static columnEditable(colObj) { return colObj.tn !== '_evolutions' || colObj.tn !== 'nc_evolutions'; } -/* + /* static extractFunctionName(query) { const reg = @@ -1687,9 +1687,10 @@ export class PgUi { break; case 'CreateTime': colProp.dt = 'timestamp'; + colProp.cdf = 'now()'; break; case 'LastModifiedTime': - colProp.dt = 'timestamp'; + colProp.cdf = 'now()'; break; case 'AutoNumber': colProp.dt = 'int'; diff --git a/packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts index 6a5998d298..9f915a5100 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts @@ -778,9 +778,11 @@ export class SnowflakeUi { break; case 'CreateTime': colProp.dt = 'TIMESTAMP'; + colProp.cdf = 'current_timestamp()'; break; case 'LastModifiedTime': colProp.dt = 'TIMESTAMP'; + colProp.cdf = 'current_timestamp()'; break; case 'AutoNumber': colProp.dt = 'INT'; diff --git a/packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts index d5f08d6093..2225e672b6 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts @@ -628,9 +628,11 @@ export class SqliteUi { break; case 'CreateTime': colProp.dt = 'datetime'; + colProp.cdf = 'CURRENT_TIMESTAMP'; break; case 'LastModifiedTime': colProp.dt = 'datetime'; + colProp.cdf = 'CURRENT_TIMESTAMP'; break; case 'AutoNumber': colProp.dt = 'integer'; diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index 0548d25c18..5abbab2074 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -1749,6 +1749,8 @@ export class ColumnsService { if (!dbColumn) { // create column in db + + const column = sqlClient } await Column.insert({ From fc729951904c29c4df6b33ccf4a258c33afec0c7 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:18 +0000 Subject: [PATCH 009/103] feat: created/modified time - column creation - WIP --- .../nocodb/src/helpers/getColumnPropsFromUIDT.ts | 6 ++++++ packages/nocodb/src/services/columns.service.ts | 12 ++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/nocodb/src/helpers/getColumnPropsFromUIDT.ts b/packages/nocodb/src/helpers/getColumnPropsFromUIDT.ts index f06e46b3e9..671b3c6508 100644 --- a/packages/nocodb/src/helpers/getColumnPropsFromUIDT.ts +++ b/packages/nocodb/src/helpers/getColumnPropsFromUIDT.ts @@ -23,6 +23,12 @@ export default async function getColumnPropsFromUIDT( ...colProp, }; + if (newColumn.uidt === UITypes.CreateTime) { + newColumn.column_name = 'created_at'; + } else if (newColumn.uidt === UITypes.LastModifiedTime) { + newColumn.column_name = 'updated_at'; + } + newColumn.dtxp = sqlUi.getDefaultLengthForDatatype(newColumn.dt); newColumn.dtxs = sqlUi.getDefaultScaleForDatatype(newColumn.dt); diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index 5abbab2074..0b05557265 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -17,8 +17,16 @@ import { } from 'nocodb-sdk'; import { pluralize, singularize } from 'inflection'; import hash from 'object-hash'; +import { isCreatedTimeOrUpdatedTimeCol } from 'nocodb-sdk/build/main/lib/UITypes'; +import type { + ColumnReqType, + LinkToAnotherColumnReqType, + LinkToAnotherRecordType, + RelationTypes, + UserType, +} from 'nocodb-sdk'; import type SqlMgrv2 from '~/db/sql-mgr/v2/SqlMgrv2'; -import type {Base, LinkToAnotherRecordColumn} from '~/models'; +import type { Base, LinkToAnotherRecordColumn } from '~/models'; import type { ColumnReqType, LinkToAnotherColumnReqType, @@ -30,6 +38,7 @@ import type CustomKnex from '~/db/CustomKnex'; import type SqlClient from '~/db/sql-client/lib/SqlClient'; import type { BaseModelSqlv2 } from '~/db/BaseModelSqlv2'; import type { NcRequest } from '~/interface/config'; +import { Column, FormulaColumn, KanbanView, Model, Source } from '~/models'; import { AppHooksService } from '~/services/app-hooks/app-hooks.service'; import formulaQueryBuilderv2 from '~/db/formulav2/formulaQueryBuilderv2'; import ProjectMgrv2 from '~/db/sql-mgr/v2/ProjectMgrv2'; @@ -63,7 +72,6 @@ import Noco from '~/Noco'; import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2'; import { MetaTable } from '~/utils/globals'; import { MetaService } from '~/meta/meta.service'; -import {isCreatedTimeOrUpdatedTimeCol} from "nocodb-sdk/build/main/lib/UITypes"; // todo: move export enum Altered { From 1df4b2c2f5241edb920ba6fc055ce75100275b7b Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:18 +0000 Subject: [PATCH 010/103] feat: created/modified time - column creation - WIP --- packages/nocodb-sdk/src/lib/index.ts | 1 + .../nocodb/src/helpers/getColumnPropsFromUIDT.ts | 16 +++++++++------- packages/nocodb/src/services/columns.service.ts | 9 +++++++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/nocodb-sdk/src/lib/index.ts b/packages/nocodb-sdk/src/lib/index.ts index 4be58508a9..78dfdf294c 100644 --- a/packages/nocodb-sdk/src/lib/index.ts +++ b/packages/nocodb-sdk/src/lib/index.ts @@ -13,6 +13,7 @@ export { isNumericCol, isVirtualCol, isLinksOrLTAR, + isCreatedTimeOrUpdatedTimeCol, } from '~/lib/UITypes'; export { default as CustomAPI, FileType } from '~/lib/CustomAPI'; export { default as TemplateGenerator } from '~/lib/TemplateGenerator'; diff --git a/packages/nocodb/src/helpers/getColumnPropsFromUIDT.ts b/packages/nocodb/src/helpers/getColumnPropsFromUIDT.ts index 671b3c6508..3210762fae 100644 --- a/packages/nocodb/src/helpers/getColumnPropsFromUIDT.ts +++ b/packages/nocodb/src/helpers/getColumnPropsFromUIDT.ts @@ -23,12 +23,6 @@ export default async function getColumnPropsFromUIDT( ...colProp, }; - if (newColumn.uidt === UITypes.CreateTime) { - newColumn.column_name = 'created_at'; - } else if (newColumn.uidt === UITypes.LastModifiedTime) { - newColumn.column_name = 'updated_at'; - } - newColumn.dtxp = sqlUi.getDefaultLengthForDatatype(newColumn.dt); newColumn.dtxs = sqlUi.getDefaultScaleForDatatype(newColumn.dt); @@ -43,5 +37,13 @@ export default async function getColumnPropsFromUIDT( newColumn.altered = column.altered || 2; - return { ...newColumn, ...column }; + const finalColumnMeta = { ...newColumn, ...column }; + + if (finalColumnMeta.uidt === UITypes.CreateTime) { + finalColumnMeta.column_name = 'created_at'; + } else if (finalColumnMeta.uidt === UITypes.LastModifiedTime) { + finalColumnMeta.column_name = 'updated_at'; + } + + return finalColumnMeta; } diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index 0b05557265..d2ed5d5143 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -8,6 +8,7 @@ import type { } from 'nocodb-sdk'; import { AppEvents, + isCreatedTimeOrUpdatedTimeCol, isLinksOrLTAR, isVirtualCol, substituteColumnAliasWithIdInFormula, @@ -17,7 +18,6 @@ import { } from 'nocodb-sdk'; import { pluralize, singularize } from 'inflection'; import hash from 'object-hash'; -import { isCreatedTimeOrUpdatedTimeCol } from 'nocodb-sdk/build/main/lib/UITypes'; import type { ColumnReqType, LinkToAnotherColumnReqType, @@ -1734,7 +1734,12 @@ export class ColumnsService { // check if column already exists, then just create a new column in meta // else create a new column in meta and db const existingColumn = await table.getColumns().then((col) => { - return col.find((c) => c.uidt === colBody.uidt); + return col.find( + (c) => + c.uidt === colBody.uidt || + c.column_name === + (c.uidt === UITypes.CreateTime ? 'created_at' : 'updated_at'), + ); }); if (!existingColumn) { From 1b46da8edf5f401220e946c5a9a1f978a1779b77 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:19 +0000 Subject: [PATCH 011/103] feat: use readonly picker for created/updated time --- .../components/cell/ReadOnlyDateTimePicker.vue | 15 +++++++++++++++ packages/nc-gui/components/smartsheet/Cell.vue | 3 ++- .../nc-gui/components/smartsheet/header/Cell.vue | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 packages/nc-gui/components/cell/ReadOnlyDateTimePicker.vue diff --git a/packages/nc-gui/components/cell/ReadOnlyDateTimePicker.vue b/packages/nc-gui/components/cell/ReadOnlyDateTimePicker.vue new file mode 100644 index 0000000000..b5579414f0 --- /dev/null +++ b/packages/nc-gui/components/cell/ReadOnlyDateTimePicker.vue @@ -0,0 +1,15 @@ + + + diff --git a/packages/nc-gui/components/smartsheet/Cell.vue b/packages/nc-gui/components/smartsheet/Cell.vue index 36acdc221e..6f8b539bec 100644 --- a/packages/nc-gui/components/smartsheet/Cell.vue +++ b/packages/nc-gui/components/smartsheet/Cell.vue @@ -1,6 +1,6 @@ diff --git a/packages/nc-gui/components/smartsheet/Cell.vue b/packages/nc-gui/components/smartsheet/Cell.vue index 6f8b539bec..7facccfe17 100644 --- a/packages/nc-gui/components/smartsheet/Cell.vue +++ b/packages/nc-gui/components/smartsheet/Cell.vue @@ -228,7 +228,6 @@ onUnmounted(() => { :disable-option-creation="!!isEditColumnMenu" :row-index="props.rowIndex" /> - import type { ColumnType } from 'nocodb-sdk' +import { isCreatedTimeOrUpdatedTimeCol } from 'nocodb-sdk' import { ActiveCellInj, CellValueInj, @@ -114,6 +115,7 @@ onUnmounted(() => { + diff --git a/packages/nocodb-sdk/src/lib/UITypes.ts b/packages/nocodb-sdk/src/lib/UITypes.ts index f9bffcb6b6..51b943dd1c 100644 --- a/packages/nocodb-sdk/src/lib/UITypes.ts +++ b/packages/nocodb-sdk/src/lib/UITypes.ts @@ -83,6 +83,8 @@ export function isVirtualCol( UITypes.Rollup, UITypes.Lookup, UITypes.Links, + UITypes.CreateTime, + UITypes.LastModifiedTime, // UITypes.Count, ].includes((typeof col === 'object' ? col?.uidt : col)); } From 4e243342b68daaffbe7f85abf97a6bfc3a304136 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:19 +0000 Subject: [PATCH 013/103] chore: cleanup --- packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts | 1 + .../nocodb/src/services/columns.service.ts | 45 +------------------ 2 files changed, 2 insertions(+), 44 deletions(-) diff --git a/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts index f9df9bdc28..64457eea79 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts @@ -1691,6 +1691,7 @@ export class PgUi { break; case 'LastModifiedTime': colProp.cdf = 'now()'; + colProp.au = true; break; case 'AutoNumber': colProp.dt = 'int'; diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index d2ed5d5143..59ec3b4f62 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -1,11 +1,4 @@ import { Injectable } from '@nestjs/common'; -import type { - ColumnReqType, - LinkToAnotherColumnReqType, - LinkToAnotherRecordType, - RelationTypes, - UserType, -} from 'nocodb-sdk'; import { AppEvents, isCreatedTimeOrUpdatedTimeCol, @@ -18,13 +11,6 @@ import { } from 'nocodb-sdk'; import { pluralize, singularize } from 'inflection'; import hash from 'object-hash'; -import type { - ColumnReqType, - LinkToAnotherColumnReqType, - LinkToAnotherRecordType, - RelationTypes, - UserType, -} from 'nocodb-sdk'; import type SqlMgrv2 from '~/db/sql-mgr/v2/SqlMgrv2'; import type { Base, LinkToAnotherRecordColumn } from '~/models'; import type { @@ -38,7 +24,6 @@ import type CustomKnex from '~/db/CustomKnex'; import type SqlClient from '~/db/sql-client/lib/SqlClient'; import type { BaseModelSqlv2 } from '~/db/BaseModelSqlv2'; import type { NcRequest } from '~/interface/config'; -import { Column, FormulaColumn, KanbanView, Model, Source } from '~/models'; import { AppHooksService } from '~/services/app-hooks/app-hooks.service'; import formulaQueryBuilderv2 from '~/db/formulav2/formulaQueryBuilderv2'; import ProjectMgrv2 from '~/db/sql-mgr/v2/ProjectMgrv2'; @@ -318,34 +303,6 @@ export class ColumnsService { ...column, title: colBody.title, }); - - if (!existingColumn) { - const sqlClient = await reuseOrSave('sqlClient', reuse, async () => - NcConnectionMgrv2.getSqlClient(source), - ); - const dbColumns = ( - await sqlClient.columnList({ - tn: table.table_name, - schema: source.getConfig()?.schema, - }) - )?.data?.list; - - // todo: check type as well - const dbColumn = dbColumns.find( - (c) => - c.column_name === - (c.uidt === UITypes.CreateTime ? 'created_at' : 'updated_at'), - ); - - if (!dbColumn) { - // create column in db - } - - await Column.insert({ - ...colBody, - fk_model_id: table.id, - }); - } } else if ( [UITypes.SingleSelect, UITypes.MultiSelect].includes(colBody.uidt) ) { @@ -1763,7 +1720,7 @@ export class ColumnsService { if (!dbColumn) { // create column in db - const column = sqlClient + const column = sqlClient; } await Column.insert({ From 6105f747c0f285924dd93630296b1bee75463fe5 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:19 +0000 Subject: [PATCH 014/103] feat: create column if missing in table(created/updated time) --- .../nocodb/src/services/columns.service.ts | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index 59ec3b4f62..9453dd257b 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -1720,7 +1720,30 @@ export class ColumnsService { if (!dbColumn) { // create column in db - const column = sqlClient; + const tableUpdateBody = { + ...table, + tn: table.table_name, + originalColumns: table.columns.map((c) => ({ + ...c, + cn: c.column_name, + })), + columns: [ + ...table.columns.map((c) => ({ ...c, cn: c.column_name })), + { + ...colBody, + cn: UITypes.CreateTime ? 'created_at' : 'updated_at', + altered: Altered.NEW_COLUMN, + }, + ], + }; + + const sqlClient = await reuseOrSave('sqlClient', reuse, async () => + NcConnectionMgrv2.getSqlClient(source), + ); + const sqlMgr = await reuseOrSave('sqlMgr', reuse, async () => + ProjectMgrv2.getSqlMgr({ id: source.base_id }), + ); + await sqlMgr.sqlOpPlus(source, 'tableUpdate', tableUpdateBody); } await Column.insert({ From fed12471c77a0d88bd63d5a1bab8ad3cc45186a5 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:19 +0000 Subject: [PATCH 015/103] feat: icon and color(created/updated time) --- .../nc-gui/components/smartsheet/header/VirtualCellIcon.ts | 3 +++ packages/nocodb/src/services/columns.service.ts | 5 ----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts b/packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts index e7e71286c4..cfa9f45d6b 100644 --- a/packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts +++ b/packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts @@ -62,6 +62,9 @@ const renderIcon = (column: ColumnType, relationColumn?: ColumnType) => { return { icon: iconMap.rollup, color: 'text-grey' } case UITypes.Count: return { icon: CountIcon, color: 'text-grey' } + case UITypes.CreateTime: + case UITypes.LastModifiedTime: + return { icon: iconMap.datetime, color: 'text-grey' } } return { icon: iconMap.generic, color: 'text-grey' } diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index 9453dd257b..7836078e44 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -1716,7 +1716,6 @@ export class ColumnsService { c.column_name === (c.uidt === UITypes.CreateTime ? 'created_at' : 'updated_at'), ); - if (!dbColumn) { // create column in db @@ -1736,10 +1735,6 @@ export class ColumnsService { }, ], }; - - const sqlClient = await reuseOrSave('sqlClient', reuse, async () => - NcConnectionMgrv2.getSqlClient(source), - ); const sqlMgr = await reuseOrSave('sqlMgr', reuse, async () => ProjectMgrv2.getSqlMgr({ id: source.base_id }), ); From 1d0e83134234b696759fa3c7dfa7ac75bc941bf1 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:19 +0000 Subject: [PATCH 016/103] feat: condition(created/updated time) --- packages/nocodb/src/db/conditionV2.ts | 37 +++++++++++++++++++++------ 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/nocodb/src/db/conditionV2.ts b/packages/nocodb/src/db/conditionV2.ts index f31a313b1a..76f318d7e9 100644 --- a/packages/nocodb/src/db/conditionV2.ts +++ b/packages/nocodb/src/db/conditionV2.ts @@ -550,7 +550,14 @@ const parseConditionV2 = async ( ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm:ssZ'; - if ([UITypes.Date, UITypes.DateTime].includes(column.uidt)) { + if ( + [ + UITypes.Date, + UITypes.DateTime, + UITypes.CreateTime, + UITypes.LastModifiedTime, + ].includes(column.uidt) + ) { let now = dayjs(new Date()); const dateFormatFromMeta = column?.meta?.date_format; if (dateFormatFromMeta && isDateMonthFormat(dateFormatFromMeta)) { @@ -661,7 +668,13 @@ const parseConditionV2 = async ( qb = qb.where(knex.raw('BINARY ?? = ?', [field, val])); } } else { - if (column.uidt === UITypes.DateTime) { + if ( + [ + UITypes.DateTime, + UITypes.CreateTime, + UITypes.LastModifiedTime, + ].includes(column.uidt) + ) { if (qb.client.config.client === 'pg') { qb = qb.where(knex.raw('??::date = ?', [field, val])); } else { @@ -945,9 +958,13 @@ const parseConditionV2 = async ( qb = qb.whereNull(customWhereClause || field); if ( !isNumericCol(column.uidt) && - ![UITypes.Date, UITypes.DateTime, UITypes.Time].includes( - column.uidt, - ) + ![ + UITypes.Date, + UITypes.CreateTime, + UITypes.LastModifiedTime, + UITypes.DateTime, + UITypes.Time, + ].includes(column.uidt) ) { qb = qb.orWhere(field, ''); } @@ -963,9 +980,13 @@ const parseConditionV2 = async ( qb = qb.whereNotNull(customWhereClause || field); if ( !isNumericCol(column.uidt) && - ![UITypes.Date, UITypes.DateTime, UITypes.Time].includes( - column.uidt, - ) + ![ + UITypes.Date, + UITypes.DateTime, + UITypes.CreateTime, + UITypes.LastModifiedTime, + UITypes.Time, + ].includes(column.uidt) ) { qb = qb.whereNot(field, ''); } From 2ecd6c1c217fcd334b239e1598a6e50552ba413f Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:19 +0000 Subject: [PATCH 017/103] feat: method for updating last modified time programmatically --- packages/nocodb/src/db/BaseModelSqlv2.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index f43e14e51a..ac09368923 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3846,7 +3846,7 @@ class BaseModelSqlv2 { } // method for validating otpions if column is single/multi select - private async validateOptions( + private async avalidateOptions( column: Column, insertOrUpdateObject: Record, ) { @@ -5515,6 +5515,20 @@ class BaseModelSqlv2 { } } + async updateLastModifiedTime({ rowIds }: { rowIds: any | any[] }) { + const columnName = 'updated_at'; + + const qb = this.dbDriver(this.tnPath).update({ + [columnName]: this.dbDriver.fn.now(), + }); + + for(const rowId of rowIds) { + qb.orWhere(await this._wherePk(rowId)); + } + + await qb; + } + async prepareNocoData(data) { if ( this.model.columns.some((c) => From 9a3a53c0d1c31705d0c6c9986135b91e99bb4c35 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:19 +0000 Subject: [PATCH 018/103] fix: typo correction --- packages/nocodb/src/db/BaseModelSqlv2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index ac09368923..7d6ff370d3 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3846,7 +3846,7 @@ class BaseModelSqlv2 { } // method for validating otpions if column is single/multi select - private async avalidateOptions( + private async validateOptions( column: Column, insertOrUpdateObject: Record, ) { From 08b867ad2260cbd1e719801970a0b28b1f5bd287 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:20 +0000 Subject: [PATCH 019/103] feat: update last modified time when updating --- packages/nocodb/src/db/BaseModelSqlv2.ts | 89 +++++++++++++++++++++++- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index 7d6ff370d3..ddee9ba865 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3974,6 +3974,15 @@ class BaseModelSqlv2 { { raw: true }, ); } + + await this.updateLastModifiedTime({ + model: parentTable, + rowIds: [childId], + }); + await this.updateLastModifiedTime({ + model: childTable, + rowIds: [rowId], + }); } break; case RelationTypes.HAS_MANY: @@ -3993,6 +4002,11 @@ class BaseModelSqlv2 { null, { raw: true }, ); + + await this.updateLastModifiedTime({ + model: parentTable, + rowIds: [rowId], + }); } break; case RelationTypes.BELONGS_TO: @@ -4012,6 +4026,11 @@ class BaseModelSqlv2 { null, { raw: true }, ); + + await this.updateLastModifiedTime({ + model: parentTable, + rowIds: [childId], + }); } break; } @@ -4105,6 +4124,15 @@ class BaseModelSqlv2 { null, { raw: true }, ); + + await this.updateLastModifiedTime({ + model: parentTable, + rowIds: [childId], + }); + await this.updateLastModifiedTime({ + model: childTable, + rowIds: [rowId], + }); } break; case RelationTypes.HAS_MANY: @@ -4122,6 +4150,11 @@ class BaseModelSqlv2 { null, { raw: true }, ); + + await this.updateLastModifiedTime({ + model: parentTable, + rowIds: [rowId], + }); } break; case RelationTypes.BELONGS_TO: @@ -4139,6 +4172,11 @@ class BaseModelSqlv2 { null, { raw: true }, ); + + await this.updateLastModifiedTime({ + model: parentTable, + rowIds: [childId], + }); } break; } @@ -5048,6 +5086,15 @@ class BaseModelSqlv2 { await this.execAndParse(this.dbDriver(vTn).insert(insertData), null, { raw: true, }); + + await this.updateLastModifiedTime({ + model: parentTable, + rowIds: childIds, + }); + await this.updateLastModifiedTime({ + model: childTable, + rowIds: [rowId], + }); } break; case RelationTypes.HAS_MANY: @@ -5122,6 +5169,11 @@ class BaseModelSqlv2 { ); } await this.execAndParse(updateQb, null, { raw: true }); + + await this.updateLastModifiedTime({ + model: parentTable, + rowIds: [rowId], + }); } break; case RelationTypes.BELONGS_TO: @@ -5164,6 +5216,11 @@ class BaseModelSqlv2 { null, { raw: true }, ); + + await this.updateLastModifiedTime({ + model: parentTable, + rowIds: [rowId], + }); } break; } @@ -5305,6 +5362,15 @@ class BaseModelSqlv2 { : childIds, ); await this.execAndParse(delQb, null, { raw: true }); + + await this.updateLastModifiedTime({ + model: parentTable, + rowIds: childIds, + }); + await this.updateLastModifiedTime({ + model: childTable, + rowIds: [rowId], + }); } break; case RelationTypes.HAS_MANY: @@ -5385,6 +5451,11 @@ class BaseModelSqlv2 { null, { raw: true }, ); + + await this.updateLastModifiedTime({ + model: parentTable, + rowIds: [rowId], + }); } break; case RelationTypes.BELONGS_TO: @@ -5430,6 +5501,11 @@ class BaseModelSqlv2 { null, { raw: true }, ); + + await this.updateLastModifiedTime({ + model: parentTable, + rowIds: [childIds[0]], + }); } break; } @@ -5515,14 +5591,21 @@ class BaseModelSqlv2 { } } - async updateLastModifiedTime({ rowIds }: { rowIds: any | any[] }) { + async updateLastModifiedTime({ + rowIds, + model = this.model, + knex = this.dbDriver, + }: { + rowIds: any | any[]; + model?: Model; + }) { const columnName = 'updated_at'; - const qb = this.dbDriver(this.tnPath).update({ + const qb = knex(model.table_name).update({ [columnName]: this.dbDriver.fn.now(), }); - for(const rowId of rowIds) { + for (const rowId of Array.isArray(rowIds) ? rowIds : [rowIds]) { qb.orWhere(await this._wherePk(rowId)); } From 2e7974ce31ac70797ce90a1624e67033f2349e8b Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:20 +0000 Subject: [PATCH 020/103] fix: missing prop in type --- packages/nocodb/src/db/BaseModelSqlv2.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index ddee9ba865..ac1c71c3ff 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -5598,6 +5598,7 @@ class BaseModelSqlv2 { }: { rowIds: any | any[]; model?: Model; + knex?: XKnex; }) { const columnName = 'updated_at'; From 2a500f1f1bc2899db4f84bbc03e9e8c5d957bc98 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:20 +0000 Subject: [PATCH 021/103] fix: use execAndParse --- packages/nocodb/src/db/BaseModelSqlv2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index ac1c71c3ff..e96136e6d6 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -5610,7 +5610,7 @@ class BaseModelSqlv2 { qb.orWhere(await this._wherePk(rowId)); } - await qb; + await this.execAndParse(qb, null, { raw: true }); } async prepareNocoData(data) { From 0007251dbb9f60b05c585ab22b1157229e1ddd6b Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:20 +0000 Subject: [PATCH 022/103] feat: populate system columns for created/updated --- packages/nocodb/src/db/BaseModelSqlv2.ts | 8 ++++++-- .../nocodb/src/services/columns.service.ts | 19 +++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index e96136e6d6..25837c2008 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -5600,10 +5600,14 @@ class BaseModelSqlv2 { model?: Model; knex?: XKnex; }) { - const columnName = 'updated_at'; + const columnName = await model.getColumns().then((columns) => { + return columns.find((c) => c.uidt === UITypes.LastModifiedTime)?.column_name; + }); + + if(!columnName) return; const qb = knex(model.table_name).update({ - [columnName]: this.dbDriver.fn.now(), + [columnName]: Noco.ncMeta.now(), }); for (const rowId of Array.isArray(rowIds) ? rowIds : [rowIds]) { diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index 7836078e44..e1c45a84e3 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -1692,10 +1692,10 @@ export class ColumnsService { // else create a new column in meta and db const existingColumn = await table.getColumns().then((col) => { return col.find( - (c) => - c.uidt === colBody.uidt || - c.column_name === - (c.uidt === UITypes.CreateTime ? 'created_at' : 'updated_at'), + (c) => c.uidt === colBody.uidt && c.system, + // || + // c.column_name === + // (c.uidt === UITypes.CreateTime ? 'created_at' : 'updated_at'), ); }); @@ -1741,6 +1741,17 @@ export class ColumnsService { await sqlMgr.sqlOpPlus(source, 'tableUpdate', tableUpdateBody); } + const title = getUniqueColumnAliasName( + table.columns, + UITypes.CreateTime ? 'CreatedAt' : 'UpdatedAt', + ); + + await Column.insert({ + ...colBody, + title, + system: 1, + fk_model_id: table.id, + }); await Column.insert({ ...colBody, fk_model_id: table.id, From e58e221047a7a666f034ba78b0022562e666da8b Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:20 +0000 Subject: [PATCH 023/103] feat: add upgrader for migrating existing --- .../nocodb/src/version-upgrader/NcUpgrader.ts | 2 + .../ncXcdbCreatedAndUpdatedTimeUpgrader.ts | 66 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 packages/nocodb/src/version-upgrader/ncXcdbCreatedAndUpdatedTimeUpgrader.ts diff --git a/packages/nocodb/src/version-upgrader/NcUpgrader.ts b/packages/nocodb/src/version-upgrader/NcUpgrader.ts index 804dc427f7..9c8c19976d 100644 --- a/packages/nocodb/src/version-upgrader/NcUpgrader.ts +++ b/packages/nocodb/src/version-upgrader/NcUpgrader.ts @@ -16,6 +16,7 @@ import ncHookUpgrader from './ncHookUpgrader'; import ncProjectConfigUpgrader from './ncProjectConfigUpgrader'; import ncXcdbLTARUpgrader from './ncXcdbLTARUpgrader'; import ncXcdbLTARIndexUpgrader from './ncXcdbLTARIndexUpgrader'; +import ncXcdbCreatedAndUpdatedTimeUpgrader from './ncXcdbCreatedAndUpdatedTimeUpgrader'; import type { MetaService } from '~/meta/meta.service'; import type { NcConfig } from '~/interface/config'; @@ -144,6 +145,7 @@ export default class NcUpgrader { { name: '0107004', handler: ncProjectConfigUpgrader }, { name: '0108002', handler: ncXcdbLTARUpgrader }, { name: '0111002', handler: ncXcdbLTARIndexUpgrader }, + { name: '0111004', handler: ncXcdbCreatedAndUpdatedTimeUpgrader }, ]; } } diff --git a/packages/nocodb/src/version-upgrader/ncXcdbCreatedAndUpdatedTimeUpgrader.ts b/packages/nocodb/src/version-upgrader/ncXcdbCreatedAndUpdatedTimeUpgrader.ts new file mode 100644 index 0000000000..912554418b --- /dev/null +++ b/packages/nocodb/src/version-upgrader/ncXcdbCreatedAndUpdatedTimeUpgrader.ts @@ -0,0 +1,66 @@ +import { UITypes } from 'nocodb-sdk'; +import { Logger } from '@nestjs/common'; +import type { NcUpgraderCtx } from './NcUpgrader'; +import type { MetaService } from '~/meta/meta.service'; +import { MetaTable } from '~/utils/globals'; +import { Column, Model } from '~/models'; + +// An upgrader for upgrading created_at and updated_at columns +// to system column and convert to new uidt CreateTime and LastModifiedTime + +const logger = new Logger('XcdbCreatedAndUpdatedTimeUpgrader'); + +async function upgradeModels({ + ncMeta, + source, +}: { + ncMeta: MetaService; + source: any; +}) { + const models = await Model.list( + { + base_id: source.base_id, + source_id: source.id, + }, + ncMeta, + ); + + await Promise.all( + models.map(async (model: any) => { + const columns = await model.getColumns(ncMeta); + for (const column of columns) { + if (column.column_name === 'created_at') { + await Column.update(column.id, { + uidt: UITypes.CreateTime, + system: true, + }); + } + if (column.uidt === 'updated_at') { + await Column.update(column.id, { + uidt: UITypes.LastModifiedTime, + system: true, + }); + } + } + logger.log(`Upgraded model ${model.name} from source ${source.name}`); + }), + ); +} + +// database to virtual relation and create an index for it +export default async function ({ ncMeta }: NcUpgraderCtx) { + // get all xcdb sources + const sources = await ncMeta.metaList2(null, null, MetaTable.BASES, { + condition: { + is_meta: 1, + }, + orderBy: {}, + }); + + // iterate and upgrade each base + for (const source of sources) { + logger.log(`Upgrading source ${source.name}`); + // update the meta props + await upgradeModels({ ncMeta, source }); + } +} From 3e4f59d11f6149d411ca50871f485ead0591a9ab Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:20 +0000 Subject: [PATCH 024/103] feat: create column with different name if already column exist --- packages/nocodb/src/db/BaseModelSqlv2.ts | 31 ++++++++--- .../nocodb/src/services/columns.service.ts | 55 ++++++++++--------- 2 files changed, 52 insertions(+), 34 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index 25837c2008..04f9054947 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -101,12 +101,23 @@ function checkColumnRequired( return !fields || fields.includes(column.title); } -export function getColumnName(column: Column) { +export function getColumnName(column: Column, columns: Column[] = []) { switch (column.uidt) { - case UITypes.CreateTime: - return 'created_at'; - case UITypes.LastModifiedTime: - return 'updated_at'; + case UITypes.CreateTime: { + const createdTimeSystemCol = columns.find( + (col) => col.system && col.uidt === UITypes.CreateTime, + ); + if (createdTimeSystemCol) return createdTimeSystemCol.column_name; + return column.column_name || 'created_at'; + } + case UITypes.LastModifiedTime: { + const lastModifiedTimeSystemCol = columns.find( + (col) => col.system && col.uidt === UITypes.LastModifiedTime, + ); + if (lastModifiedTimeSystemCol) + return lastModifiedTimeSystemCol.column_name; + return column.column_name || 'created_at'; + } default: return column.column_name; } @@ -2128,7 +2139,10 @@ class BaseModelSqlv2 { case UITypes.LastModifiedTime: case UITypes.DateTime: { - const columnName = getColumnName(column); + const columnName = getColumnName( + column, + await this.model.getColumns(), + ); if (this.isMySQL) { // MySQL stores timestamp in UTC but display in timezone // To verify the timezone, run `SELECT @@global.time_zone, @@session.time_zone;` @@ -5601,10 +5615,11 @@ class BaseModelSqlv2 { knex?: XKnex; }) { const columnName = await model.getColumns().then((columns) => { - return columns.find((c) => c.uidt === UITypes.LastModifiedTime)?.column_name; + return columns.find((c) => c.uidt === UITypes.LastModifiedTime) + ?.column_name; }); - if(!columnName) return; + if (!columnName) return; const qb = knex(model.table_name).update({ [columnName]: Noco.ncMeta.now(), diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index e1c45a84e3..4c735a1280 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -1688,34 +1688,36 @@ export class ColumnsService { case UITypes.CreateTime: case UITypes.LastModifiedTime: { + const columns = await table.getColumns(); // check if column already exists, then just create a new column in meta // else create a new column in meta and db - const existingColumn = await table.getColumns().then((col) => { - return col.find( - (c) => c.uidt === colBody.uidt && c.system, - // || - // c.column_name === - // (c.uidt === UITypes.CreateTime ? 'created_at' : 'updated_at'), - ); - }); + const existingColumn = columns.find( + (c) => c.uidt === colBody.uidt && c.system, + // || + // c.column_name === + // (c.uidt === UITypes.CreateTime ? 'created_at' : 'updated_at'), + ); if (!existingColumn) { - const sqlClient = await reuseOrSave('sqlClient', reuse, async () => - NcConnectionMgrv2.getSqlClient(source), - ); - const dbColumns = ( - await sqlClient.columnList({ - tn: table.table_name, - schema: source.getConfig()?.schema, - }) - )?.data?.list; + let columnName = + colBody.uidt === UITypes.CreateTime ? 'created_at' : 'updated_at'; + // const sqlClient = await reuseOrSave('sqlClient', reuse, async () => + // NcConnectionMgrv2.getSqlClient(source), + // ); + // const dbColumns = ( + // await sqlClient.columnList({ + // tn: table.table_name, + // schema: source.getConfig()?.schema, + // }) + // )?.data?.list; // todo: check type as well - const dbColumn = dbColumns.find( - (c) => - c.column_name === - (c.uidt === UITypes.CreateTime ? 'created_at' : 'updated_at'), - ); + const dbColumn = columns.find((c) => c.column_name === columnName); + + if (dbColumn) { + columnName = getUniqueColumnName(columns, columnName); + } + if (!dbColumn) { // create column in db @@ -1752,11 +1754,11 @@ export class ColumnsService { system: 1, fk_model_id: table.id, }); - await Column.insert({ - ...colBody, - fk_model_id: table.id, - }); } + await Column.insert({ + ...colBody, + fk_model_id: table.id, + }); } break; default: @@ -2775,6 +2777,7 @@ export class ColumnsService { await Column.update(column.id, colBody); } } + async columnsHash(tableId: string) { const table = await Model.getWithInfo({ id: tableId, From d4c9cbf74ae0fe6710df0dd2076564537b0d4794 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:20 +0000 Subject: [PATCH 025/103] feat: populate created and updated time whils inserting / updating , programmatically --- packages/nocodb/src/db/BaseModelSqlv2.ts | 44 +++++++++++++------ .../nocodb/src/services/columns.service.ts | 18 ++++++-- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index 04f9054947..3abdca693d 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -18,6 +18,7 @@ import Validator from 'validator'; import { customAlphabet } from 'nanoid'; import DOMPurify from 'isomorphic-dompurify'; import { v4 as uuidv4 } from 'uuid'; +import type { SortType } from 'nocodb-sdk'; import type { Knex } from 'knex'; import type LookupColumn from '~/models/LookupColumn'; import type { XKnex } from '~/db/CustomKnex'; @@ -35,15 +36,6 @@ import type { SelectOption, User, } from '~/models'; -import type { SortType } from 'nocodb-sdk'; -import formulaQueryBuilderv2 from '~/db/formulav2/formulaQueryBuilderv2'; -import genRollupSelectv2 from '~/db/genRollupSelectv2'; -import conditionV2 from '~/db/conditionV2'; -import sortV2 from '~/db/sortV2'; -import { customValidators } from '~/db/util/customValidators'; -import { extractLimitAndOffset } from '~/helpers'; -import { NcError } from '~/helpers/catchError'; -import getAst from '~/helpers/getAst'; import { Audit, BaseUser, @@ -55,6 +47,14 @@ import { Source, View, } from '~/models'; +import formulaQueryBuilderv2 from '~/db/formulav2/formulaQueryBuilderv2'; +import genRollupSelectv2 from '~/db/genRollupSelectv2'; +import conditionV2 from '~/db/conditionV2'; +import sortV2 from '~/db/sortV2'; +import { customValidators } from '~/db/util/customValidators'; +import { extractLimitAndOffset } from '~/helpers'; +import { NcError } from '~/helpers/catchError'; +import getAst from '~/helpers/getAst'; import { sanitize, unsanitize } from '~/helpers/sqlSanitize'; import Noco from '~/Noco'; import { HANDLE_WEBHOOK } from '~/services/hook-handler.service'; @@ -2358,7 +2358,7 @@ class BaseModelSqlv2 { await this.beforeInsert(insertObj, trx, cookie); } - await this.prepareNocoData(insertObj); + await this.prepareNocoData(insertObj, true); let response; // const driver = trx ? trx : this.dbDriver; @@ -2709,7 +2709,6 @@ class BaseModelSqlv2 { this.clientMeta, this.dbDriver, ); - let rowId = null; const nestedCols = (await this.model.getColumns()).filter((c) => @@ -2725,6 +2724,8 @@ class BaseModelSqlv2 { await this.beforeInsert(insertObj, this.dbDriver, cookie); + await this.prepareNocoData(insertObj, true); + let response; const query = this.dbDriver(this.tnPath).insert(insertObj); @@ -3068,7 +3069,7 @@ class BaseModelSqlv2 { } } - await this.prepareNocoData(insertObj); + await this.prepareNocoData(insertObj, true); // prepare nested link data for insert only if it is single record insertion if (isSingleRecordInsertion) { @@ -5632,13 +5633,28 @@ class BaseModelSqlv2 { await this.execAndParse(qb, null, { raw: true }); } - async prepareNocoData(data) { + async prepareNocoData(data, isInsertData = false) { if ( this.model.columns.some((c) => - [UITypes.Attachment, UITypes.User].includes(c.uidt), + [ + UITypes.Attachment, + UITypes.User, + UITypes.CreateTime, + UITypes.LastModifiedTime, + ].includes(c.uidt), ) ) { for (const column of this.model.columns) { + if ( + isInsertData && + column.uidt === UITypes.CreateTime && + column.system + ) { + data[column.column_name] = Noco.ncMeta.now(); + } + if (column.uidt === UITypes.LastModifiedTime && column.system) { + data[column.column_name] = Noco.ncMeta.now(); + } if (column.uidt === UITypes.Attachment) { if (data[column.column_name]) { if (Array.isArray(data[column.column_name])) { diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index 4c735a1280..bd1ff10969 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -1688,6 +1688,7 @@ export class ColumnsService { case UITypes.CreateTime: case UITypes.LastModifiedTime: { + let columnName: string; const columns = await table.getColumns(); // check if column already exists, then just create a new column in meta // else create a new column in meta and db @@ -1699,7 +1700,7 @@ export class ColumnsService { ); if (!existingColumn) { - let columnName = + columnName = colBody.uidt === UITypes.CreateTime ? 'created_at' : 'updated_at'; // const sqlClient = await reuseOrSave('sqlClient', reuse, async () => // NcConnectionMgrv2.getSqlClient(source), @@ -1718,9 +1719,14 @@ export class ColumnsService { columnName = getUniqueColumnName(columns, columnName); } - if (!dbColumn) { - // create column in db + { + // remove default value for SQLite since it doesn't support default value as function when adding column + // only support default value as constant value + if (source.type === 'sqlite3') { + colBody.cdf = null; + } + // create column in db const tableUpdateBody = { ...table, tn: table.table_name, @@ -1732,7 +1738,7 @@ export class ColumnsService { ...table.columns.map((c) => ({ ...c, cn: c.column_name })), { ...colBody, - cn: UITypes.CreateTime ? 'created_at' : 'updated_at', + cn: columnName, altered: Altered.NEW_COLUMN, }, ], @@ -1753,11 +1759,15 @@ export class ColumnsService { title, system: 1, fk_model_id: table.id, + column_name: columnName, }); + } else { + columnName = existingColumn.column_name; } await Column.insert({ ...colBody, fk_model_id: table.id, + column_name: columnName, }); } break; From 60610e1e0191594d839f1f014aed576328711512 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:20 +0000 Subject: [PATCH 026/103] feat: change the default created/updated column type and mark as system field --- packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts | 6 ++++-- packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts | 10 ++++++---- packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts | 6 ++++-- packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts | 6 ++++-- packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts | 10 ++++++---- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts index 02f4e96acf..fd623f67be 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts @@ -107,9 +107,10 @@ export class MssqlUi { dtxp: '', dtxs: '', altered: 1, - uidt: UITypes.DateTime, + uidt: UITypes.CreateTime, uip: '', uicn: '', + system: true, }, { column_name: 'updated_at', @@ -131,9 +132,10 @@ export class MssqlUi { dtxp: '', dtxs: '', altered: 1, - uidt: UITypes.DateTime, + uidt: UITypes.LastModifiedTime, uip: '', uicn: '', + system: true, }, ]; } diff --git a/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts index 1b8cc0c8eb..9ab2fbe3b3 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts @@ -111,9 +111,10 @@ export class MysqlUi { dtxp: '', dtxs: '', altered: 1, - uidt: UITypes.DateTime, + uidt: UITypes.CreateTime, uip: '', uicn: '', + system: true, }, { column_name: 'updated_at', @@ -134,9 +135,10 @@ export class MysqlUi { dtxp: '', dtxs: '', altered: 1, - uidt: UITypes.DateTime, + uidt: UITypes.LastModifiedTime, uip: '', uicn: '', + system: true, }, ]; } @@ -1073,11 +1075,11 @@ export class MysqlUi { break; case 'CreateTime': colProp.dt = 'timestamp'; - colProp.cdf = 'CURRENT_TIMESTAMP' + colProp.cdf = 'CURRENT_TIMESTAMP'; break; case 'LastModifiedTime': colProp.dt = 'timestamp'; - colProp.cdf = 'CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP' + colProp.cdf = 'CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP'; break; case 'AutoNumber': colProp.dt = 'int'; diff --git a/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts index 64457eea79..20f026ff3c 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts @@ -170,9 +170,10 @@ export class PgUi { dtxp: '', dtxs: '', altered: 1, - uidt: UITypes.DateTime, + uidt: UITypes.CreateTime, uip: '', uicn: '', + system: true, }, { column_name: 'updated_at', @@ -194,9 +195,10 @@ export class PgUi { dtxp: '', dtxs: '', altered: 1, - uidt: UITypes.DateTime, + uidt: UITypes.LastModifiedTime, uip: '', uicn: '', + system: true, }, ]; } diff --git a/packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts index 9f915a5100..48737ee8ba 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts @@ -106,9 +106,10 @@ export class SnowflakeUi { dtxp: '', dtxs: '', altered: 1, - uidt: UITypes.DateTime, + uidt: UITypes.CreateTime, uip: '', uicn: '', + system: true, }, { column_name: 'updated_at', @@ -130,9 +131,10 @@ export class SnowflakeUi { dtxp: '', dtxs: '', altered: 1, - uidt: UITypes.DateTime, + uidt: UITypes.LastModifiedTime, uip: '', uicn: '', + system: true, }, ]; } diff --git a/packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts index 2225e672b6..4869dd2bb9 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts @@ -94,9 +94,10 @@ export class SqliteUi { dtxp: '', dtxs: '', altered: 1, - uidt: UITypes.DateTime, + uidt: UITypes.CreateTime, uip: '', uicn: '', + system: true, }, { column_name: 'updated_at', @@ -117,9 +118,10 @@ export class SqliteUi { dtxp: '', dtxs: '', altered: 1, - uidt: UITypes.DateTime, + uidt: UITypes.LastModifiedTime, uip: '', uicn: '', + system: true, }, ]; } @@ -628,11 +630,11 @@ export class SqliteUi { break; case 'CreateTime': colProp.dt = 'datetime'; - colProp.cdf = 'CURRENT_TIMESTAMP'; + colProp.cdf = 'CURRENT_TIMESTAMP'; break; case 'LastModifiedTime': colProp.dt = 'datetime'; - colProp.cdf = 'CURRENT_TIMESTAMP'; + colProp.cdf = 'CURRENT_TIMESTAMP'; break; case 'AutoNumber': colProp.dt = 'integer'; From 64bdee82bf321ecaa83f4f8e1356c1141ed75c4a Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:20 +0000 Subject: [PATCH 027/103] refactor: keep column name only in system field --- packages/nocodb/src/services/columns.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index bd1ff10969..a2dbb446a6 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -1767,7 +1767,7 @@ export class ColumnsService { await Column.insert({ ...colBody, fk_model_id: table.id, - column_name: columnName, + column_name: null, }); } break; From 54f6748fd3bf2f3105c36414f42a3979580b1c6c Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:21 +0000 Subject: [PATCH 028/103] refactor: block system column deletion --- packages/nocodb/src/services/columns.service.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index a2dbb446a6..0c7f203cdd 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -2212,6 +2212,11 @@ export class ColumnsService { case UITypes.CreateTime: case UITypes.LastModifiedTime: { + if (column.system) { + NcError.badRequest( + `The column '${column.column_name}' is a system column and cannot be deleted.`, + ); + } await Column.delete(param.columnId, ncMeta); } break; From 1b708da05b45fad12247f84a0892e8372cd11cb3 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Jan 2024 13:19:21 +0000 Subject: [PATCH 029/103] feat: hide dlete option from menu for created/updated system column --- packages/nc-gui/components/smartsheet/header/Menu.vue | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/nc-gui/components/smartsheet/header/Menu.vue b/packages/nc-gui/components/smartsheet/header/Menu.vue index c3460abe27..6a1a5e1299 100644 --- a/packages/nc-gui/components/smartsheet/header/Menu.vue +++ b/packages/nc-gui/components/smartsheet/header/Menu.vue @@ -1,6 +1,7 @@