Files
MyGoNavi/frontend/src/utils/columnDefinition.ts
Syngnat c9d0bce153 🐛 fix(workbench): 修复结果区切换与对象设计字段定义
- 修复手动隐藏结果区后快捷键无法再次显示的问题

- 补全 Dameng/Oracle-like 字段长度、精度和可空标识

- 增加 SQL 编辑器、字段元数据和对象设计回归测试
2026-06-10 15:51:03 +08:00

177 lines
5.7 KiB
TypeScript

import type { ColumnDefinition } from '../types';
const readStringProperty = (value: unknown, keys: string[]): string => {
const source = value as Record<string, unknown> | null | undefined;
if (!source || typeof source !== 'object') return '';
for (const key of keys) {
const raw = source[key];
if (raw !== undefined && raw !== null) {
return String(raw).trim();
}
}
for (const [sourceKey, raw] of Object.entries(source)) {
if (keys.some((key) => sourceKey.toLowerCase() === key.toLowerCase())) {
return raw === undefined || raw === null ? '' : String(raw).trim();
}
}
return '';
};
const readProperty = (value: unknown, keys: string[]): unknown => {
const source = value as Record<string, unknown> | null | undefined;
if (!source || typeof source !== 'object') return undefined;
for (const key of keys) {
if (source[key] !== undefined && source[key] !== null) {
return source[key];
}
}
for (const [sourceKey, raw] of Object.entries(source)) {
if (keys.some((key) => sourceKey.toLowerCase() === key.toLowerCase())) {
return raw;
}
}
return undefined;
};
const readBooleanProperty = (value: unknown, keys: string[]): boolean => {
const raw = readProperty(value, keys);
if (raw === undefined || raw === null) return false;
if (typeof raw === 'boolean') return raw;
if (typeof raw === 'number') return raw !== 0;
const text = String(raw).trim().toLowerCase();
return text === '1' || text === 't' || text === 'true' || text === 'y' || text === 'yes' || text === 'pri' || text === 'primary';
};
const readNumberProperty = (value: unknown, keys: string[]): number => {
const raw = readProperty(value, keys);
if (raw === undefined || raw === null || raw === '') return 0;
const parsed = Number(raw);
return Number.isFinite(parsed) && parsed > 0 ? Math.trunc(parsed) : 0;
};
const normalizeNullable = (value: string): string => {
const normalized = String(value || '').trim();
if (!normalized) return '';
const upper = normalized.toUpperCase();
if (upper === 'N' || upper === 'NO' || upper === 'FALSE' || upper === '0' || upper === 'NOT NULL') {
return 'NO';
}
if (upper === 'Y' || upper === 'YES' || upper === 'TRUE' || upper === '1' || upper === 'NULL' || upper === 'NULLABLE') {
return 'YES';
}
return normalized;
};
export const getColumnDefinitionName = (column: unknown): string => (
readStringProperty(column, ['name', 'Name', 'COLUMN_NAME', 'column_name', 'field', 'Field'])
);
export const getColumnDefinitionType = (column: unknown): string => {
const fullType = readStringProperty(column, [
'COLUMN_TYPE',
'column_type',
'FULL_TYPE',
'full_type',
'FULL_DATA_TYPE',
'full_data_type',
'TYPE_NAME',
'type_name',
'Type',
'type',
]);
if (fullType) return fullType;
const dataType = readStringProperty(column, ['DATA_TYPE', 'data_type']);
if (!dataType || /\(.+\)/.test(dataType)) return dataType;
const upperType = dataType.toUpperCase();
const charLength = readNumberProperty(column, [
'CHARACTER_MAXIMUM_LENGTH',
'character_maximum_length',
'CHARACTER_MAX_LENGTH',
'character_max_length',
'CHAR_LENGTH',
'char_length',
'DATA_LENGTH',
'data_length',
'LENGTH',
'length',
]);
if (charLength > 0 && /(CHAR|VARCHAR|BINARY|VARBINARY|NCHAR|NVARCHAR)/.test(upperType)) {
return `${dataType}(${charLength})`;
}
const precision = readNumberProperty(column, [
'NUMERIC_PRECISION',
'numeric_precision',
'DATA_PRECISION',
'data_precision',
'PRECISION',
'precision',
]);
if (precision > 0 && /(DECIMAL|NUMERIC|NUMBER)/.test(upperType)) {
const scale = readNumberProperty(column, [
'NUMERIC_SCALE',
'numeric_scale',
'DATA_SCALE',
'data_scale',
'SCALE',
'scale',
]);
return scale > 0 ? `${dataType}(${precision},${scale})` : `${dataType}(${precision})`;
}
return dataType;
};
export const getColumnDefinitionKey = (column: unknown): string => {
const key = readStringProperty(column, ['key', 'Key', 'COLUMN_KEY', 'column_key']);
if (key) {
const normalized = key.trim();
const lowered = normalized.toLowerCase();
if (lowered === 'pri' || lowered === 'primary' || lowered === 'primary key') return 'PRI';
if (lowered === 'uni' || lowered === 'unique') return 'UNI';
if (lowered === 'mul' || lowered === 'multiple') return 'MUL';
return normalized;
}
if (readBooleanProperty(column, ['primaryKey', 'primary_key', 'isPrimary', 'is_primary', 'IS_PRIMARY', 'pk', 'PK'])) {
return 'PRI';
}
if (readBooleanProperty(column, ['unique', 'isUnique', 'is_unique', 'UNIQUE', 'IS_UNIQUE'])) {
return 'UNI';
}
return '';
};
export const getColumnDefinitionExtra = (column: unknown): string => (
readStringProperty(column, ['extra', 'Extra'])
);
export const getColumnDefinitionComment = (column: unknown): string => (
readStringProperty(column, ['comment', 'Comment', 'COMMENTS', 'comments', 'COLUMN_COMMENT', 'column_comment'])
);
export const normalizeColumnDefinition = (column: unknown): ColumnDefinition => {
const source = (column && typeof column === 'object' ? column : {}) as Partial<ColumnDefinition>;
return {
...source,
name: getColumnDefinitionName(column),
type: getColumnDefinitionType(column),
nullable: normalizeNullable(readStringProperty(column, ['nullable', 'Nullable', 'NULLABLE', 'is_nullable', 'IS_NULLABLE', 'Null', 'null'])),
key: getColumnDefinitionKey(column),
default: source.default,
extra: getColumnDefinitionExtra(column),
comment: getColumnDefinitionComment(column),
};
};
export const normalizeColumnDefinitions = (columns: unknown): ColumnDefinition[] => (
Array.isArray(columns) ? columns.map(normalizeColumnDefinition) : []
);