mirror of
				https://github.com/chartdb/chartdb.git
				synced 2025-11-03 21:43:23 +00:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			bcd8aa9378
			...
			jf/add_sup
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					21ba816a6d | 
@@ -143,6 +143,7 @@ export const en = {
 | 
				
			|||||||
                        title: 'Field Attributes',
 | 
					                        title: 'Field Attributes',
 | 
				
			||||||
                        unique: 'Unique',
 | 
					                        unique: 'Unique',
 | 
				
			||||||
                        auto_increment: 'Auto Increment',
 | 
					                        auto_increment: 'Auto Increment',
 | 
				
			||||||
 | 
					                        array: 'Declare Array',
 | 
				
			||||||
                        character_length: 'Max Length',
 | 
					                        character_length: 'Max Length',
 | 
				
			||||||
                        precision: 'Precision',
 | 
					                        precision: 'Precision',
 | 
				
			||||||
                        scale: 'Scale',
 | 
					                        scale: 'Scale',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -165,3 +165,21 @@ export const supportsAutoIncrementDataType = (
 | 
				
			|||||||
        'decimal',
 | 
					        'decimal',
 | 
				
			||||||
    ].includes(dataTypeName.toLocaleLowerCase());
 | 
					    ].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 '[]')
 | 
					                        // Handle array types (check if the field has array property or type name ends with '[]')
 | 
				
			||||||
                        if (typeName.endsWith('[]')) {
 | 
					                        if (field.array || typeName.endsWith('[]')) {
 | 
				
			||||||
                            typeWithSize =
 | 
					                            if (!typeName.endsWith('[]')) {
 | 
				
			||||||
                                typeWithSize.replace('[]', '') + '[]';
 | 
					                                typeWithSize = typeWithSize + '[]';
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                typeWithSize =
 | 
				
			||||||
 | 
					                                    typeWithSize.replace('[]', '') + '[]';
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        const notNull = field.nullable ? '' : ' NOT NULL';
 | 
					                        const notNull = field.nullable ? '' : ' NOT NULL';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,6 +29,7 @@ export interface SQLColumn {
 | 
				
			|||||||
    comment?: string;
 | 
					    comment?: string;
 | 
				
			||||||
    default?: string;
 | 
					    default?: string;
 | 
				
			||||||
    increment?: boolean;
 | 
					    increment?: boolean;
 | 
				
			||||||
 | 
					    array?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface SQLTable {
 | 
					export interface SQLTable {
 | 
				
			||||||
@@ -612,6 +613,7 @@ export function convertToChartDBDiagram(
 | 
				
			|||||||
                default: column.default || '',
 | 
					                default: column.default || '',
 | 
				
			||||||
                createdAt: Date.now(),
 | 
					                createdAt: Date.now(),
 | 
				
			||||||
                increment: column.increment,
 | 
					                increment: column.increment,
 | 
				
			||||||
 | 
					                array: column.array,
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Add type arguments if present
 | 
					            // Add type arguments if present
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -373,6 +373,13 @@ function extractColumnsFromSQL(sql: string): SQLColumn[] {
 | 
				
			|||||||
                'SMALLSERIAL',
 | 
					                'SMALLSERIAL',
 | 
				
			||||||
            ].includes(upperType.split('(')[0]);
 | 
					            ].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
 | 
					            // Normalize the type
 | 
				
			||||||
            columnType = normalizePostgreSQLType(columnType);
 | 
					            columnType = normalizePostgreSQLType(columnType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -395,6 +402,7 @@ function extractColumnsFromSQL(sql: string): SQLColumn[] {
 | 
				
			|||||||
                    trimmedLine.includes('uuid_generate_v4()') ||
 | 
					                    trimmedLine.includes('uuid_generate_v4()') ||
 | 
				
			||||||
                    trimmedLine.includes('GENERATED ALWAYS AS IDENTITY') ||
 | 
					                    trimmedLine.includes('GENERATED ALWAYS AS IDENTITY') ||
 | 
				
			||||||
                    trimmedLine.includes('GENERATED BY DEFAULT AS IDENTITY'),
 | 
					                    trimmedLine.includes('GENERATED BY DEFAULT AS IDENTITY'),
 | 
				
			||||||
 | 
					                array: isArrayType,
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -782,6 +790,16 @@ export async function fromPostgres(
 | 
				
			|||||||
                                    normalizePostgreSQLType(rawDataType);
 | 
					                                    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
 | 
					                            // Now handle parameters - but skip for integer types that shouldn't have them
 | 
				
			||||||
                            let finalDataType = normalizedBaseType;
 | 
					                            let finalDataType = normalizedBaseType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -874,6 +892,7 @@ export async function fromPostgres(
 | 
				
			|||||||
                                            stmt.sql
 | 
					                                            stmt.sql
 | 
				
			||||||
                                                .toUpperCase()
 | 
					                                                .toUpperCase()
 | 
				
			||||||
                                                .includes('IDENTITY')),
 | 
					                                                .includes('IDENTITY')),
 | 
				
			||||||
 | 
					                                    array: isArrayType,
 | 
				
			||||||
                                });
 | 
					                                });
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        } else if (def.resource === 'constraint') {
 | 
					                        } else if (def.resource === 'constraint') {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@ export interface DBField {
 | 
				
			|||||||
    unique: boolean;
 | 
					    unique: boolean;
 | 
				
			||||||
    nullable: boolean;
 | 
					    nullable: boolean;
 | 
				
			||||||
    increment?: boolean | null;
 | 
					    increment?: boolean | null;
 | 
				
			||||||
 | 
					    array?: boolean | null;
 | 
				
			||||||
    createdAt: number;
 | 
					    createdAt: number;
 | 
				
			||||||
    characterMaximumLength?: string | null;
 | 
					    characterMaximumLength?: string | null;
 | 
				
			||||||
    precision?: number | null;
 | 
					    precision?: number | null;
 | 
				
			||||||
@@ -36,6 +37,7 @@ export const dbFieldSchema: z.ZodType<DBField> = z.object({
 | 
				
			|||||||
    unique: z.boolean(),
 | 
					    unique: z.boolean(),
 | 
				
			||||||
    nullable: z.boolean(),
 | 
					    nullable: z.boolean(),
 | 
				
			||||||
    increment: z.boolean().or(z.null()).optional(),
 | 
					    increment: z.boolean().or(z.null()).optional(),
 | 
				
			||||||
 | 
					    array: z.boolean().or(z.null()).optional(),
 | 
				
			||||||
    createdAt: z.number(),
 | 
					    createdAt: z.number(),
 | 
				
			||||||
    characterMaximumLength: z.string().or(z.null()).optional(),
 | 
					    characterMaximumLength: z.string().or(z.null()).optional(),
 | 
				
			||||||
    precision: z.number().or(z.null()).optional(),
 | 
					    precision: z.number().or(z.null()).optional(),
 | 
				
			||||||
@@ -71,13 +73,48 @@ export const createFieldsFromMetadata = ({
 | 
				
			|||||||
        pk.column.trim()
 | 
					        pk.column.trim()
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return sortedColumns.map(
 | 
					    return sortedColumns.map((col: ColumnInfo): DBField => {
 | 
				
			||||||
        (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(),
 | 
					            id: generateId(),
 | 
				
			||||||
            name: col.name,
 | 
					            name: col.name,
 | 
				
			||||||
            type: {
 | 
					            type: {
 | 
				
			||||||
                id: col.type.split(' ').join('_').toLowerCase(),
 | 
					                id: baseType.split(' ').join('_').toLowerCase(),
 | 
				
			||||||
                name: col.type.toLowerCase(),
 | 
					                name: baseType.toLowerCase(),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            primaryKey: tablePrimaryKeysColumns.includes(col.name),
 | 
					            primaryKey: tablePrimaryKeysColumns.includes(col.name),
 | 
				
			||||||
            unique: Object.values(aggregatedIndexes).some(
 | 
					            unique: Object.values(aggregatedIndexes).some(
 | 
				
			||||||
@@ -87,20 +124,18 @@ export const createFieldsFromMetadata = ({
 | 
				
			|||||||
                    idx.columns[0].name === col.name
 | 
					                    idx.columns[0].name === col.name
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            nullable: Boolean(col.nullable),
 | 
					            nullable: Boolean(col.nullable),
 | 
				
			||||||
            ...(col.character_maximum_length &&
 | 
					            ...(isArrayType ? { array: true } : {}),
 | 
				
			||||||
            col.character_maximum_length !== 'null'
 | 
					            ...(charMaxLength && charMaxLength !== 'null'
 | 
				
			||||||
                ? { characterMaximumLength: col.character_maximum_length }
 | 
					                ? { characterMaximumLength: charMaxLength }
 | 
				
			||||||
                : {}),
 | 
					                : {}),
 | 
				
			||||||
            ...(col.precision?.precision
 | 
					            ...(precision ? { precision } : {}),
 | 
				
			||||||
                ? { precision: col.precision.precision }
 | 
					            ...(scale ? { scale } : {}),
 | 
				
			||||||
                : {}),
 | 
					 | 
				
			||||||
            ...(col.precision?.scale ? { scale: col.precision.scale } : {}),
 | 
					 | 
				
			||||||
            ...(col.default ? { default: col.default } : {}),
 | 
					            ...(col.default ? { default: col.default } : {}),
 | 
				
			||||||
            ...(col.collation ? { collation: col.collation } : {}),
 | 
					            ...(col.collation ? { collation: col.collation } : {}),
 | 
				
			||||||
            createdAt: Date.now(),
 | 
					            createdAt: Date.now(),
 | 
				
			||||||
            comments: col.comment ? col.comment : undefined,
 | 
					            comments: col.comment ? col.comment : undefined,
 | 
				
			||||||
        })
 | 
					        };
 | 
				
			||||||
    );
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const generateDBFieldSuffix = (
 | 
					export const generateDBFieldSuffix = (
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ import type { FieldAttributeRange } from '@/lib/data/data-types/data-types';
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
    findDataTypeDataById,
 | 
					    findDataTypeDataById,
 | 
				
			||||||
    supportsAutoIncrementDataType,
 | 
					    supportsAutoIncrementDataType,
 | 
				
			||||||
 | 
					    supportsArrayDataType,
 | 
				
			||||||
} from '@/lib/data/data-types/data-types';
 | 
					} from '@/lib/data/data-types/data-types';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    Popover,
 | 
					    Popover,
 | 
				
			||||||
@@ -87,6 +88,7 @@ export const TableFieldPopover: React.FC<TableFieldPopoverProps> = ({
 | 
				
			|||||||
                unique: localField.unique,
 | 
					                unique: localField.unique,
 | 
				
			||||||
                default: localField.default,
 | 
					                default: localField.default,
 | 
				
			||||||
                increment: localField.increment,
 | 
					                increment: localField.increment,
 | 
				
			||||||
 | 
					                array: localField.array,
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        prevFieldRef.current = localField;
 | 
					        prevFieldRef.current = localField;
 | 
				
			||||||
@@ -102,6 +104,13 @@ export const TableFieldPopover: React.FC<TableFieldPopoverProps> = ({
 | 
				
			|||||||
        [field.type.name]
 | 
					        [field.type.name]
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const supportsArray = useMemo(
 | 
				
			||||||
 | 
					        () =>
 | 
				
			||||||
 | 
					            databaseType === 'postgresql' &&
 | 
				
			||||||
 | 
					            supportsArrayDataType(field.type.name),
 | 
				
			||||||
 | 
					        [field.type.name, databaseType]
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Popover
 | 
					        <Popover
 | 
				
			||||||
            open={isOpen}
 | 
					            open={isOpen}
 | 
				
			||||||
@@ -168,6 +177,27 @@ export const TableFieldPopover: React.FC<TableFieldPopoverProps> = ({
 | 
				
			|||||||
                                />
 | 
					                                />
 | 
				
			||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
                        ) : null}
 | 
					                        ) : 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">
 | 
					                        <div className="flex flex-col gap-2">
 | 
				
			||||||
                            <Label htmlFor="default" className="text-subtitle">
 | 
					                            <Label htmlFor="default" className="text-subtitle">
 | 
				
			||||||
                                {t(
 | 
					                                {t(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ import type { DataTypeData } from '@/lib/data/data-types/data-types';
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
    dataTypeDataToDataType,
 | 
					    dataTypeDataToDataType,
 | 
				
			||||||
    sortedDataTypeMap,
 | 
					    sortedDataTypeMap,
 | 
				
			||||||
 | 
					    supportsArrayDataType,
 | 
				
			||||||
} from '@/lib/data/data-types/data-types';
 | 
					} from '@/lib/data/data-types/data-types';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    Tooltip,
 | 
					    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({
 | 
					            updateField({
 | 
				
			||||||
                characterMaximumLength,
 | 
					                characterMaximumLength,
 | 
				
			||||||
                precision,
 | 
					                precision,
 | 
				
			||||||
@@ -185,6 +193,7 @@ export const TableField: React.FC<TableFieldProps> = ({
 | 
				
			|||||||
                        name: value as string,
 | 
					                        name: value as string,
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
 | 
					                ...(shouldClearArray ? { array: false } : {}),
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        [
 | 
					        [
 | 
				
			||||||
@@ -193,6 +202,7 @@ export const TableField: React.FC<TableFieldProps> = ({
 | 
				
			|||||||
            field.characterMaximumLength,
 | 
					            field.characterMaximumLength,
 | 
				
			||||||
            field.precision,
 | 
					            field.precision,
 | 
				
			||||||
            field.scale,
 | 
					            field.scale,
 | 
				
			||||||
 | 
					            field.array,
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user