diff --git a/api/src/services/authorization.ts b/api/src/services/authorization.ts index 2884aac776..276516c015 100644 --- a/api/src/services/authorization.ts +++ b/api/src/services/authorization.ts @@ -247,9 +247,9 @@ export class AuthorizationService { specials.includes(name) ); - const notNullable = field.nullable === false && hasGenerateSpecial === false; + const nullable = field.nullable || hasGenerateSpecial || field.generated; - if (notNullable) { + if (!nullable) { requiredColumns.push(field); } } diff --git a/api/src/types/schema.ts b/api/src/types/schema.ts index 614794f128..a6fa3f9d8b 100644 --- a/api/src/types/schema.ts +++ b/api/src/types/schema.ts @@ -5,6 +5,7 @@ export type FieldOverview = { field: string; defaultValue: any; nullable: boolean; + generated: boolean; type: Type | 'unknown' | 'alias'; dbType: string | null; precision: number | null; diff --git a/api/src/utils/get-schema.ts b/api/src/utils/get-schema.ts index aa1ee03f7c..8525617610 100644 --- a/api/src/utils/get-schema.ts +++ b/api/src/utils/get-schema.ts @@ -154,6 +154,7 @@ async function getDatabaseSchema( field: column.column_name, defaultValue: getDefaultValue(column) ?? null, nullable: column.is_nullable ?? true, + generated: column.is_generated ?? false, type: getLocalType(column).type, dbType: column.data_type, precision: column.numeric_precision || null, @@ -191,6 +192,7 @@ async function getDatabaseSchema( field: field.field, defaultValue: existing?.defaultValue ?? null, nullable: existing?.nullable ?? true, + generated: existing?.generated ?? false, type: type, dbType: existing?.dbType || null, precision: existing?.precision || null, diff --git a/app/src/modules/settings/routes/data-model/field-detail/field-detail-advanced/field-detail-advanced-field.vue b/app/src/modules/settings/routes/data-model/field-detail/field-detail-advanced/field-detail-advanced-field.vue index a3f5d03080..205b19a2ce 100644 --- a/app/src/modules/settings/routes/data-model/field-detail/field-detail-advanced/field-detail-advanced-field.vue +++ b/app/src/modules/settings/routes/data-model/field-detail/field-detail-advanced/field-detail-advanced-field.vue @@ -1,18 +1,18 @@ -
+
{{ t('default_value') }}
@@ -136,12 +136,12 @@
{{ t('nullable') }}
- +
{{ t('unique') }}
- +
@@ -279,6 +279,10 @@ export default defineComponent({ return fieldDetailStore.field.schema?.is_primary_key === true; }); + const isGenerated = computed(() => { + return fieldDetailStore.field.schema?.is_generated; + }); + return { t, typesWithLabels, @@ -303,6 +307,7 @@ export default defineComponent({ unique, isPrimaryKey, isExisting, + isGenerated, }; function useOnCreate() { diff --git a/packages/schema/src/dialects/mssql.ts b/packages/schema/src/dialects/mssql.ts index 58b8d2ab62..a15d7c6856 100644 --- a/packages/schema/src/dialects/mssql.ts +++ b/packages/schema/src/dialects/mssql.ts @@ -15,8 +15,10 @@ export default class MSSQL extends KnexMSSQL implements SchemaInspector { c.IS_NULLABLE as is_nullable, c.DATA_TYPE as data_type, c.CHARACTER_MAXIMUM_LENGTH as max_length, + cc.is_computed as is_generated, pk.PK_SET as column_key, - COLUMNPROPERTY(OBJECT_ID(c.TABLE_SCHEMA + '.' + c.TABLE_NAME), c.COLUMN_NAME, 'IsIdentity') as is_identity + COLUMNPROPERTY(OBJECT_ID(c.TABLE_SCHEMA + '.' + c.TABLE_NAME), c.COLUMN_NAME, 'IsIdentity') as is_identity, + COLUMNPROPERTY(OBJECT_ID(c.TABLE_SCHEMA + '.' + c.TABLE_NAME), c.COLUMN_NAME, 'IsComputed') as is_generated FROM [${this.knex.client.database()}].INFORMATION_SCHEMA.COLUMNS as c LEFT JOIN ( diff --git a/packages/schema/src/dialects/mysql.ts b/packages/schema/src/dialects/mysql.ts index 78838e8494..c5c45f9e80 100644 --- a/packages/schema/src/dialects/mysql.ts +++ b/packages/schema/src/dialects/mysql.ts @@ -53,6 +53,7 @@ export default class MySQL extends KnexMySQL implements SchemaInspector { ...column, default_value: column.extra === 'auto_increment' ? 'AUTO_INCREMENT' : parseDefaultValue(column.default_value), is_nullable: column.is_nullable === 'YES', + is_generated: column.extra?.endsWith('GENERATED') ?? false, data_type: dataType, }; } diff --git a/packages/schema/src/dialects/oracledb.ts b/packages/schema/src/dialects/oracledb.ts index 02c2d526e0..24e08ccbd8 100644 --- a/packages/schema/src/dialects/oracledb.ts +++ b/packages/schema/src/dialects/oracledb.ts @@ -36,6 +36,7 @@ export default class Oracle extends KnexOracle implements SchemaInspector { column_name: string; default_value: string; is_nullable: string; + is_generated: string; data_type: string; numeric_precision: number | null; numeric_scale: number | null; @@ -62,7 +63,8 @@ export default class Oracle extends KnexOracle implements SchemaInspector { "c"."DATA_PRECISION" "numeric_precision", "c"."DATA_SCALE" "numeric_scale", "ct"."CONSTRAINT_TYPE" "column_key", - "c"."CHAR_LENGTH" "max_length" + "c"."CHAR_LENGTH" "max_length", + "c"."VIRTUAL_COLUMN" "is_generated" FROM "USER_TAB_COLUMNS" "c" LEFT JOIN "uc" "ct" ON "c"."TABLE_NAME" = "ct"."TABLE_NAME" AND "c"."COLUMN_NAME" = "ct"."COLUMN_NAME" @@ -88,6 +90,7 @@ export default class Oracle extends KnexOracle implements SchemaInspector { overview[column.table_name].columns[column.column_name] = { ...column, is_nullable: column.is_nullable === 'Y', + is_generated: column.is_generated === 'YES', default_value: hasAutoIncrement ? 'AUTO_INCREMENT' : stripQuotes(column.default_value), }; } diff --git a/packages/schema/src/dialects/postgres.ts b/packages/schema/src/dialects/postgres.ts index 752ad7124f..3b3f8a77f6 100644 --- a/packages/schema/src/dialects/postgres.ts +++ b/packages/schema/src/dialects/postgres.ts @@ -13,6 +13,7 @@ export default class Postgres extends KnexPostgres implements SchemaInspector { max_length: number | null; is_identity: boolean; is_nullable: boolean; + is_generated: boolean; }; type RawGeometryColumn = { @@ -31,6 +32,7 @@ export default class Postgres extends KnexPostgres implements SchemaInspector { , c.column_default as default_value , c.data_type , c.character_maximum_length as max_length + , c.is_generated = 'ALWAYS' is_generated , CASE WHEN c.is_identity = 'YES' THEN true ELSE false END is_identity , CASE WHEN c.is_nullable = 'YES' THEN true ELSE false END is_nullable FROM diff --git a/packages/schema/src/dialects/sqlite.ts b/packages/schema/src/dialects/sqlite.ts index 5af794489d..54518a4fe9 100644 --- a/packages/schema/src/dialects/sqlite.ts +++ b/packages/schema/src/dialects/sqlite.ts @@ -10,6 +10,7 @@ type RawColumn = { name: string; type: string; notnull: 0 | 1; + hidden: 0 | 1 | 2; dflt_value: any; pk: 0 | 1; }; @@ -24,7 +25,7 @@ export default class SQLite extends KnexSQLite implements SchemaInspector { const overview: SchemaOverview = {}; for (const table of tables) { - const columns = await this.knex.raw(`PRAGMA table_info(??)`, table); + const columns = await this.knex.raw(`PRAGMA table_xinfo(??)`, table); if (table in overview === false) { overview[table] = { @@ -42,6 +43,7 @@ export default class SQLite extends KnexSQLite implements SchemaInspector { ? 'AUTO_INCREMENT' : stripQuotes(column.dflt_value), is_nullable: column.notnull == 0, + is_generated: column.hidden !== 0, data_type: extractType(column.type), max_length: extractMaxLength(column.type), numeric_precision: null, diff --git a/packages/schema/src/types/overview.ts b/packages/schema/src/types/overview.ts index 260ec56eb8..5b02cb04d8 100644 --- a/packages/schema/src/types/overview.ts +++ b/packages/schema/src/types/overview.ts @@ -7,6 +7,7 @@ export type SchemaOverview = { column_name: string; default_value: any; is_nullable: boolean; + is_generated: boolean; data_type: string; numeric_precision?: number | null; numeric_scale?: number | null;