mirror of
				https://github.com/chartdb/chartdb.git
				synced 2025-11-04 05:53:15 +00:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			v1.15.1
			...
			jf/add_sup
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					21ba816a6d | 
@@ -143,6 +143,7 @@ export const en = {
 | 
			
		||||
                        title: 'Field Attributes',
 | 
			
		||||
                        unique: 'Unique',
 | 
			
		||||
                        auto_increment: 'Auto Increment',
 | 
			
		||||
                        array: 'Declare Array',
 | 
			
		||||
                        character_length: 'Max Length',
 | 
			
		||||
                        precision: 'Precision',
 | 
			
		||||
                        scale: 'Scale',
 | 
			
		||||
 
 | 
			
		||||
@@ -165,3 +165,21 @@ export const supportsAutoIncrementDataType = (
 | 
			
		||||
        'decimal',
 | 
			
		||||
    ].includes(dataTypeName.toLocaleLowerCase());
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const supportsArrayDataType = (dataTypeName: string): boolean => {
 | 
			
		||||
    // Types that do NOT support arrays in PostgreSQL
 | 
			
		||||
    const unsupportedTypes = [
 | 
			
		||||
        'serial',
 | 
			
		||||
        'bigserial',
 | 
			
		||||
        'smallserial',
 | 
			
		||||
        'serial2',
 | 
			
		||||
        'serial4',
 | 
			
		||||
        'serial8',
 | 
			
		||||
        'xml',
 | 
			
		||||
        'money',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    // Check if the type is in the unsupported list
 | 
			
		||||
    const normalizedType = dataTypeName.toLowerCase();
 | 
			
		||||
    return !unsupportedTypes.includes(normalizedType);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -286,10 +286,14 @@ export function exportPostgreSQL({
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        // Handle array types (check if the type name ends with '[]')
 | 
			
		||||
                        if (typeName.endsWith('[]')) {
 | 
			
		||||
                            typeWithSize =
 | 
			
		||||
                                typeWithSize.replace('[]', '') + '[]';
 | 
			
		||||
                        // Handle array types (check if the field has array property or type name ends with '[]')
 | 
			
		||||
                        if (field.array || typeName.endsWith('[]')) {
 | 
			
		||||
                            if (!typeName.endsWith('[]')) {
 | 
			
		||||
                                typeWithSize = typeWithSize + '[]';
 | 
			
		||||
                            } else {
 | 
			
		||||
                                typeWithSize =
 | 
			
		||||
                                    typeWithSize.replace('[]', '') + '[]';
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        const notNull = field.nullable ? '' : ' NOT NULL';
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ export interface SQLColumn {
 | 
			
		||||
    comment?: string;
 | 
			
		||||
    default?: string;
 | 
			
		||||
    increment?: boolean;
 | 
			
		||||
    array?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface SQLTable {
 | 
			
		||||
@@ -612,6 +613,7 @@ export function convertToChartDBDiagram(
 | 
			
		||||
                default: column.default || '',
 | 
			
		||||
                createdAt: Date.now(),
 | 
			
		||||
                increment: column.increment,
 | 
			
		||||
                array: column.array,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Add type arguments if present
 | 
			
		||||
 
 | 
			
		||||
@@ -373,6 +373,13 @@ function extractColumnsFromSQL(sql: string): SQLColumn[] {
 | 
			
		||||
                'SMALLSERIAL',
 | 
			
		||||
            ].includes(upperType.split('(')[0]);
 | 
			
		||||
 | 
			
		||||
            // Check if it's an array type
 | 
			
		||||
            let isArrayType = false;
 | 
			
		||||
            if (columnType.endsWith('[]')) {
 | 
			
		||||
                isArrayType = true;
 | 
			
		||||
                columnType = columnType.slice(0, -2);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Normalize the type
 | 
			
		||||
            columnType = normalizePostgreSQLType(columnType);
 | 
			
		||||
 | 
			
		||||
@@ -395,6 +402,7 @@ function extractColumnsFromSQL(sql: string): SQLColumn[] {
 | 
			
		||||
                    trimmedLine.includes('uuid_generate_v4()') ||
 | 
			
		||||
                    trimmedLine.includes('GENERATED ALWAYS AS IDENTITY') ||
 | 
			
		||||
                    trimmedLine.includes('GENERATED BY DEFAULT AS IDENTITY'),
 | 
			
		||||
                array: isArrayType,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -782,6 +790,16 @@ export async function fromPostgres(
 | 
			
		||||
                                    normalizePostgreSQLType(rawDataType);
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            // Check if it's an array type
 | 
			
		||||
                            let isArrayType = false;
 | 
			
		||||
                            if (normalizedBaseType.endsWith('[]')) {
 | 
			
		||||
                                isArrayType = true;
 | 
			
		||||
                                normalizedBaseType = normalizedBaseType.slice(
 | 
			
		||||
                                    0,
 | 
			
		||||
                                    -2
 | 
			
		||||
                                );
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            // Now handle parameters - but skip for integer types that shouldn't have them
 | 
			
		||||
                            let finalDataType = normalizedBaseType;
 | 
			
		||||
 | 
			
		||||
@@ -874,6 +892,7 @@ export async function fromPostgres(
 | 
			
		||||
                                            stmt.sql
 | 
			
		||||
                                                .toUpperCase()
 | 
			
		||||
                                                .includes('IDENTITY')),
 | 
			
		||||
                                    array: isArrayType,
 | 
			
		||||
                                });
 | 
			
		||||
                            }
 | 
			
		||||
                        } else if (def.resource === 'constraint') {
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ export interface DBField {
 | 
			
		||||
    unique: boolean;
 | 
			
		||||
    nullable: boolean;
 | 
			
		||||
    increment?: boolean | null;
 | 
			
		||||
    array?: boolean | null;
 | 
			
		||||
    createdAt: number;
 | 
			
		||||
    characterMaximumLength?: string | null;
 | 
			
		||||
    precision?: number | null;
 | 
			
		||||
@@ -36,6 +37,7 @@ export const dbFieldSchema: z.ZodType<DBField> = z.object({
 | 
			
		||||
    unique: z.boolean(),
 | 
			
		||||
    nullable: z.boolean(),
 | 
			
		||||
    increment: z.boolean().or(z.null()).optional(),
 | 
			
		||||
    array: z.boolean().or(z.null()).optional(),
 | 
			
		||||
    createdAt: z.number(),
 | 
			
		||||
    characterMaximumLength: z.string().or(z.null()).optional(),
 | 
			
		||||
    precision: z.number().or(z.null()).optional(),
 | 
			
		||||
@@ -71,13 +73,48 @@ export const createFieldsFromMetadata = ({
 | 
			
		||||
        pk.column.trim()
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return sortedColumns.map(
 | 
			
		||||
        (col: ColumnInfo): DBField => ({
 | 
			
		||||
    return sortedColumns.map((col: ColumnInfo): DBField => {
 | 
			
		||||
        // Check if type is an array (ends with [])
 | 
			
		||||
        const isArrayType = col.type.endsWith('[]');
 | 
			
		||||
        let baseType = col.type;
 | 
			
		||||
 | 
			
		||||
        // Extract base type and any parameters if it's an array
 | 
			
		||||
        if (isArrayType) {
 | 
			
		||||
            baseType = col.type.slice(0, -2); // Remove the [] suffix
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Extract parameters from types like "character varying(100)" or "numeric(10,2)"
 | 
			
		||||
        let charMaxLength = col.character_maximum_length;
 | 
			
		||||
        let precision = col.precision?.precision;
 | 
			
		||||
        let scale = col.precision?.scale;
 | 
			
		||||
 | 
			
		||||
        // Handle types with single parameter like varchar(100)
 | 
			
		||||
        const singleParamMatch = baseType.match(/^(.+?)\((\d+)\)$/);
 | 
			
		||||
        if (singleParamMatch) {
 | 
			
		||||
            baseType = singleParamMatch[1];
 | 
			
		||||
            if (!charMaxLength || charMaxLength === 'null') {
 | 
			
		||||
                charMaxLength = singleParamMatch[2];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Handle types with two parameters like numeric(10,2)
 | 
			
		||||
        const twoParamMatch = baseType.match(/^(.+?)\((\d+),\s*(\d+)\)$/);
 | 
			
		||||
        if (twoParamMatch) {
 | 
			
		||||
            baseType = twoParamMatch[1];
 | 
			
		||||
            if (!precision) {
 | 
			
		||||
                precision = parseInt(twoParamMatch[2]);
 | 
			
		||||
            }
 | 
			
		||||
            if (!scale) {
 | 
			
		||||
                scale = parseInt(twoParamMatch[3]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            id: generateId(),
 | 
			
		||||
            name: col.name,
 | 
			
		||||
            type: {
 | 
			
		||||
                id: col.type.split(' ').join('_').toLowerCase(),
 | 
			
		||||
                name: col.type.toLowerCase(),
 | 
			
		||||
                id: baseType.split(' ').join('_').toLowerCase(),
 | 
			
		||||
                name: baseType.toLowerCase(),
 | 
			
		||||
            },
 | 
			
		||||
            primaryKey: tablePrimaryKeysColumns.includes(col.name),
 | 
			
		||||
            unique: Object.values(aggregatedIndexes).some(
 | 
			
		||||
@@ -87,20 +124,18 @@ export const createFieldsFromMetadata = ({
 | 
			
		||||
                    idx.columns[0].name === col.name
 | 
			
		||||
            ),
 | 
			
		||||
            nullable: Boolean(col.nullable),
 | 
			
		||||
            ...(col.character_maximum_length &&
 | 
			
		||||
            col.character_maximum_length !== 'null'
 | 
			
		||||
                ? { characterMaximumLength: col.character_maximum_length }
 | 
			
		||||
            ...(isArrayType ? { array: true } : {}),
 | 
			
		||||
            ...(charMaxLength && charMaxLength !== 'null'
 | 
			
		||||
                ? { characterMaximumLength: charMaxLength }
 | 
			
		||||
                : {}),
 | 
			
		||||
            ...(col.precision?.precision
 | 
			
		||||
                ? { precision: col.precision.precision }
 | 
			
		||||
                : {}),
 | 
			
		||||
            ...(col.precision?.scale ? { scale: col.precision.scale } : {}),
 | 
			
		||||
            ...(precision ? { precision } : {}),
 | 
			
		||||
            ...(scale ? { scale } : {}),
 | 
			
		||||
            ...(col.default ? { default: col.default } : {}),
 | 
			
		||||
            ...(col.collation ? { collation: col.collation } : {}),
 | 
			
		||||
            createdAt: Date.now(),
 | 
			
		||||
            comments: col.comment ? col.comment : undefined,
 | 
			
		||||
        })
 | 
			
		||||
    );
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const generateDBFieldSuffix = (
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import type { FieldAttributeRange } from '@/lib/data/data-types/data-types';
 | 
			
		||||
import {
 | 
			
		||||
    findDataTypeDataById,
 | 
			
		||||
    supportsAutoIncrementDataType,
 | 
			
		||||
    supportsArrayDataType,
 | 
			
		||||
} from '@/lib/data/data-types/data-types';
 | 
			
		||||
import {
 | 
			
		||||
    Popover,
 | 
			
		||||
@@ -87,6 +88,7 @@ export const TableFieldPopover: React.FC<TableFieldPopoverProps> = ({
 | 
			
		||||
                unique: localField.unique,
 | 
			
		||||
                default: localField.default,
 | 
			
		||||
                increment: localField.increment,
 | 
			
		||||
                array: localField.array,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        prevFieldRef.current = localField;
 | 
			
		||||
@@ -102,6 +104,13 @@ export const TableFieldPopover: React.FC<TableFieldPopoverProps> = ({
 | 
			
		||||
        [field.type.name]
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const supportsArray = useMemo(
 | 
			
		||||
        () =>
 | 
			
		||||
            databaseType === 'postgresql' &&
 | 
			
		||||
            supportsArrayDataType(field.type.name),
 | 
			
		||||
        [field.type.name, databaseType]
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <Popover
 | 
			
		||||
            open={isOpen}
 | 
			
		||||
@@ -168,6 +177,27 @@ export const TableFieldPopover: React.FC<TableFieldPopoverProps> = ({
 | 
			
		||||
                                />
 | 
			
		||||
                            </div>
 | 
			
		||||
                        ) : null}
 | 
			
		||||
                        {supportsArray ? (
 | 
			
		||||
                            <div className="flex items-center justify-between">
 | 
			
		||||
                                <Label
 | 
			
		||||
                                    htmlFor="array"
 | 
			
		||||
                                    className="text-subtitle"
 | 
			
		||||
                                >
 | 
			
		||||
                                    {t(
 | 
			
		||||
                                        'side_panel.tables_section.table.field_actions.array'
 | 
			
		||||
                                    )}
 | 
			
		||||
                                </Label>
 | 
			
		||||
                                <Checkbox
 | 
			
		||||
                                    checked={localField.array ?? false}
 | 
			
		||||
                                    onCheckedChange={(value) =>
 | 
			
		||||
                                        setLocalField((current) => ({
 | 
			
		||||
                                            ...current,
 | 
			
		||||
                                            array: !!value,
 | 
			
		||||
                                        }))
 | 
			
		||||
                                    }
 | 
			
		||||
                                />
 | 
			
		||||
                            </div>
 | 
			
		||||
                        ) : null}
 | 
			
		||||
                        <div className="flex flex-col gap-2">
 | 
			
		||||
                            <Label htmlFor="default" className="text-subtitle">
 | 
			
		||||
                                {t(
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import type { DataTypeData } from '@/lib/data/data-types/data-types';
 | 
			
		||||
import {
 | 
			
		||||
    dataTypeDataToDataType,
 | 
			
		||||
    sortedDataTypeMap,
 | 
			
		||||
    supportsArrayDataType,
 | 
			
		||||
} from '@/lib/data/data-types/data-types';
 | 
			
		||||
import {
 | 
			
		||||
    Tooltip,
 | 
			
		||||
@@ -175,6 +176,13 @@ export const TableField: React.FC<TableFieldProps> = ({
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Check if the new type supports arrays - if not, clear the array property
 | 
			
		||||
            const newTypeName = dataType?.name ?? (value as string);
 | 
			
		||||
            const shouldClearArray =
 | 
			
		||||
                databaseType === 'postgresql' &&
 | 
			
		||||
                !supportsArrayDataType(newTypeName) &&
 | 
			
		||||
                field.array;
 | 
			
		||||
 | 
			
		||||
            updateField({
 | 
			
		||||
                characterMaximumLength,
 | 
			
		||||
                precision,
 | 
			
		||||
@@ -185,6 +193,7 @@ export const TableField: React.FC<TableFieldProps> = ({
 | 
			
		||||
                        name: value as string,
 | 
			
		||||
                    }
 | 
			
		||||
                ),
 | 
			
		||||
                ...(shouldClearArray ? { array: false } : {}),
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        [
 | 
			
		||||
@@ -193,6 +202,7 @@ export const TableField: React.FC<TableFieldProps> = ({
 | 
			
		||||
            field.characterMaximumLength,
 | 
			
		||||
            field.precision,
 | 
			
		||||
            field.scale,
 | 
			
		||||
            field.array,
 | 
			
		||||
        ]
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user