diff --git a/src/lib/data/import-metadata/scripts/postgres-script.ts b/src/lib/data/import-metadata/scripts/postgres-script.ts index 111f95dc..c57ceded 100644 --- a/src/lib/data/import-metadata/scripts/postgres-script.ts +++ b/src/lib/data/import-metadata/scripts/postgres-script.ts @@ -165,7 +165,7 @@ cols AS ( '","table":"', cols.table_name, '","name":"', cols.column_name, '","ordinal_position":', cols.ordinal_position, - ',"type":"', LOWER(replace(cols.data_type, '"', '')), + ',"type":"', case when LOWER(replace(cols.data_type, '"', '')) = 'user-defined' then pg_type.typname else LOWER(replace(cols.data_type, '"', '')) end, '","character_maximum_length":"', COALESCE(cols.character_maximum_length::text, 'null'), '","precision":', CASE @@ -184,8 +184,12 @@ cols AS ( ON c.relname = cols.table_name JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace AND n.nspname = cols.table_schema - LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid - AND dsc.objsubid = cols.ordinal_position + LEFT JOIN pg_catalog.pg_description dsc + ON dsc.objoid = c.oid AND dsc.objsubid = cols.ordinal_position + LEFT JOIN pg_catalog.pg_attribute attr + ON attr.attrelid = c.oid AND attr.attname = cols.column_name + LEFT JOIN pg_catalog.pg_type + ON pg_type.oid = attr.atttypid WHERE cols.table_schema NOT IN ('information_schema', 'pg_catalog')${ databaseEdition === DatabaseEdition.POSTGRESQL_TIMESCALE ? timescaleColFilter diff --git a/src/lib/domain/db-custom-type.ts b/src/lib/domain/db-custom-type.ts index dd4ca17f..e43e4c39 100644 --- a/src/lib/domain/db-custom-type.ts +++ b/src/lib/domain/db-custom-type.ts @@ -58,6 +58,3 @@ export const customTypeKindToLabel: Record = { enum: 'Enum', composite: 'Composite', }; - -export const getCustomTypeId = (name: string) => - `custom-type-${name.toLowerCase().trim()}`; diff --git a/src/lib/domain/db-field.ts b/src/lib/domain/db-field.ts index 20ef9ad1..c89f7538 100644 --- a/src/lib/domain/db-field.ts +++ b/src/lib/domain/db-field.ts @@ -7,7 +7,6 @@ import type { PrimaryKeyInfo } from '../data/import-metadata/metadata-types/prim import type { TableInfo } from '../data/import-metadata/metadata-types/table-info'; import type { DBCustomTypeInfo } from '../data/import-metadata/metadata-types/custom-type-info'; import { schemaNameToDomainSchemaName } from './db-schema'; -import { getCustomTypeId } from './db-custom-type'; export interface DBField { id: string; @@ -43,56 +42,12 @@ export const dbFieldSchema: z.ZodType = z.object({ comments: z.string().optional(), }); -// Helper function to find the best matching custom type for a column -const findMatchingCustomType = ( - columnName: string, - customTypes: DBCustomTypeInfo[] -): DBCustomTypeInfo | undefined => { - // 1. Exact name match (highest priority) - const exactMatch = customTypes.find((ct) => ct.type === columnName); - if (exactMatch) return exactMatch; - - // 2. Check if column name is the base of a custom type (e.g., 'role' matches 'role_enum') - const prefixMatch = customTypes.find((ct) => - ct.type.startsWith(columnName + '_') - ); - if (prefixMatch) return prefixMatch; - - // 3. Check if a custom type name is the base of the column name (e.g., 'user_role' matches 'role_enum') - const baseTypeMatch = customTypes.find((ct) => { - // Extract base name by removing common suffixes - const baseTypeName = ct.type.replace(/_enum$|_type$/, ''); - return ( - columnName.includes(baseTypeName) || - baseTypeName.includes(columnName) - ); - }); - if (baseTypeMatch) return baseTypeMatch; - - // 4. For composite types, check if any field matches the column name - const compositeMatch = customTypes.find( - (ct) => - ct.kind === 'composite' && - ct.fields?.some((f) => f.field === columnName) - ); - if (compositeMatch) return compositeMatch; - - // 5. Special case for name/fullname relationship which is common - if (columnName === 'name') { - const fullNameType = customTypes.find((ct) => ct.type === 'full_name'); - if (fullNameType) return fullNameType; - } - - return undefined; -}; - export const createFieldsFromMetadata = ({ columns, tableSchema, tableInfo, primaryKeys, aggregatedIndexes, - customTypes = [], }: { columns: ColumnInfo[]; tableSchema?: string; @@ -126,50 +81,14 @@ export const createFieldsFromMetadata = ({ ) .map((pk) => pk.column.trim()); - // Create a mapping between column names and custom types - const typeMap: Record = {}; - - if (customTypes && customTypes.length > 0) { - // Filter to custom types in this schema - const schemaCustomTypes = customTypes.filter( - (ct) => schemaNameToDomainSchemaName(ct.schema) === tableSchema - ); - - // Process user-defined columns to find matching custom types - for (const column of sortedColumns) { - if (column.type.toLowerCase() === 'user-defined') { - const matchingType = findMatchingCustomType( - column.name, - schemaCustomTypes - ); - if (matchingType) { - typeMap[column.name] = matchingType; - } - } - } - } - - return sortedColumns.map((col: ColumnInfo): DBField => { - let type: DataType; - - // Use custom type if available, otherwise use standard type - if (col.type.toLowerCase() === 'user-defined' && typeMap[col.name]) { - const customType = typeMap[col.name]; - type = { - id: getCustomTypeId(customType.type), - name: customType.type, - }; - } else { - type = { - id: col.type.split(' ').join('_').toLowerCase(), - name: col.type.toLowerCase(), - }; - } - - return { + return sortedColumns.map( + (col: ColumnInfo): DBField => ({ id: generateId(), name: col.name, - type, + type: { + id: col.type.split(' ').join('_').toLowerCase(), + name: col.type.toLowerCase(), + }, primaryKey: tablePrimaryKeys.includes(col.name), unique: Object.values(aggregatedIndexes).some( (idx) => @@ -190,6 +109,6 @@ export const createFieldsFromMetadata = ({ ...(col.collation ? { collation: col.collation } : {}), createdAt: Date.now(), comments: col.comment ? col.comment : undefined, - }; - }); + }) + ); }; diff --git a/src/lib/domain/db-table.ts b/src/lib/domain/db-table.ts index 7aca090a..fd998f19 100644 --- a/src/lib/domain/db-table.ts +++ b/src/lib/domain/db-table.ts @@ -102,7 +102,6 @@ export const createTablesFromMetadata = ({ columns, indexes, views: views, - custom_types: customTypes, } = databaseMetadata; return tableInfos.map((tableInfo: TableInfo) => { @@ -121,10 +120,6 @@ export const createTablesFromMetadata = ({ primaryKeys, tableInfo, tableSchema, - customTypes: - databaseType === DatabaseType.POSTGRESQL - ? customTypes - : undefined, }); const dbIndexes = createIndexesFromMetadata({ diff --git a/src/pages/editor-page/side-panel/custom-types-section/custom-type-list/custom-type-list-item/custom-type-list-item-content/composite-fields/composite-fields.tsx b/src/pages/editor-page/side-panel/custom-types-section/custom-type-list/custom-type-list-item/custom-type-list-item-content/composite-fields/composite-fields.tsx index 476c8acd..7e4fabdf 100644 --- a/src/pages/editor-page/side-panel/custom-types-section/custom-type-list/custom-type-list-item/custom-type-list-item-content/composite-fields/composite-fields.tsx +++ b/src/pages/editor-page/side-panel/custom-types-section/custom-type-list/custom-type-list-item/custom-type-list-item-content/composite-fields/composite-fields.tsx @@ -55,7 +55,7 @@ export const CustomTypeCompositeFields: React.FC< const customDataTypes = useMemo( () => customTypes.map((type) => ({ - id: `custom-type-${type.id}`, + id: type.name, name: type.name, })), [customTypes] diff --git a/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-field/table-field.tsx b/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-field/table-field.tsx index c77bacf9..bc5a5f60 100644 --- a/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-field/table-field.tsx +++ b/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-field/table-field.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback } from 'react'; import { GripVertical, KeyRound } from 'lucide-react'; import { Input } from '@/components/input/input'; import type { DBField } from '@/lib/domain/db-field'; @@ -6,7 +6,6 @@ import { useChartDB } from '@/hooks/use-chartdb'; import { dataTypeDataToDataType, sortedDataTypeMap, - type DataType, } from '@/lib/data/data-types/data-types'; import { Tooltip, @@ -35,98 +34,51 @@ export const TableField: React.FC = ({ updateField, removeField, }) => { - const { databaseType, customTypes } = useChartDB(); + const { databaseType } = useChartDB(); const { t } = useTranslation(); const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: field.id }); - const dataFieldOptions = useMemo(() => { - // Start with the built-in data types - const standardTypes: SelectBoxOption[] = sortedDataTypeMap[ - databaseType - ].map((type) => ({ - label: type.name, - value: type.id, - regex: type.hasCharMaxLength - ? `^${type.name}\\(\\d+\\)$` - : undefined, - extractRegex: type.hasCharMaxLength ? /\((\d+)\)/ : undefined, - group: 'Standard Types', - })); - - if (!customTypes?.length) { - return standardTypes; - } - - // Add custom types as options - const customTypeOptions: SelectBoxOption[] = customTypes.map( - (type) => ({ - label: type.name, - value: `custom-type-${type.name}`, - // Add additional info to distinguish custom types - description: - type.kind === 'enum' ? `${type.values?.join(' | ')}` : '', - // Group custom types together - group: 'Custom Types', - }) - ); - - return [...standardTypes, ...customTypeOptions]; - }, [databaseType, customTypes]); + const dataFieldOptions: SelectBoxOption[] = sortedDataTypeMap[ + databaseType + ].map((type) => ({ + label: type.name, + value: type.id, + regex: type.hasCharMaxLength ? `^${type.name}\\(\\d+\\)$` : undefined, + extractRegex: type.hasCharMaxLength ? /\((\d+)\)/ : undefined, + })); const onChangeDataType = useCallback< NonNullable >( (value, regexMatches) => { - // Check if this is a custom type - const isCustomType = - typeof value === 'string' && value.startsWith('custom-type-'); - - let newType: DataType; - - if (isCustomType && typeof value === 'string') { - // For custom types, create a custom DataType object - const typeName = value.replace('custom-type-', ''); - newType = { - id: value as string, - name: typeName, - }; - } else { - // For standard types, use the existing logic - const dataType = sortedDataTypeMap[databaseType].find( - (v) => v.id === value - ) ?? { - id: value as string, - name: value as string, - }; - - newType = dataTypeDataToDataType(dataType); - } + const dataType = sortedDataTypeMap[databaseType].find( + (v) => v.id === value + ) ?? { + id: value as string, + name: value as string, + }; let characterMaximumLength: string | undefined = undefined; - if (regexMatches?.length && !isCustomType) { - const dataType = sortedDataTypeMap[databaseType].find( - (v) => v.id === value - ); - - if (dataType?.hasCharMaxLength) { - characterMaximumLength = regexMatches[1]; - } - } else if (field.characterMaximumLength && !isCustomType) { - const dataType = sortedDataTypeMap[databaseType].find( - (v) => v.id === value - ); - - if (dataType?.hasCharMaxLength) { - characterMaximumLength = field.characterMaximumLength; - } + if (regexMatches?.length && dataType?.hasCharMaxLength) { + characterMaximumLength = regexMatches[1]; + } else if ( + field.characterMaximumLength && + dataType?.hasCharMaxLength + ) { + characterMaximumLength = field.characterMaximumLength; } updateField({ characterMaximumLength, - type: newType, + type: dataTypeDataToDataType( + dataType ?? { + id: value as string, + name: value as string, + } + ), }); }, [updateField, databaseType, field.characterMaximumLength] @@ -137,21 +89,6 @@ export const TableField: React.FC = ({ transition, }; - // Helper to get the display value for custom types - const getCustomTypeDisplayValue = () => { - if (field.type.id.startsWith('custom-type-')) { - const typeName = field.type.name; - const customType = customTypes?.find((ct) => ct.name === typeName); - - if (customType) { - return typeName; - } - } - return undefined; - }; - - const customTypeDisplayValue = getCustomTypeDisplayValue(); - return (
= ({ )} value={field.type.id} valueSuffix={ - customTypeDisplayValue - ? `` - : field.characterMaximumLength - ? `(${field.characterMaximumLength})` - : '' + field.characterMaximumLength + ? `(${field.characterMaximumLength})` + : '' } optionSuffix={(option) => { - // For custom types, we don't need to add a suffix - if ( - option.value - .toString() - .startsWith('custom-type-') - ) { - return ''; - } - const type = sortedDataTypeMap[ databaseType ].find((v) => v.id === option.value); @@ -236,12 +162,10 @@ export const TableField: React.FC = ({ - {customTypeDisplayValue || - `${field.type.name}${ - field.characterMaximumLength - ? `(${field.characterMaximumLength})` - : '' - }`} + {field.type.name} + {field.characterMaximumLength + ? `(${field.characterMaximumLength})` + : ''}