feat(datatypes): Add decimal / numeric attribute support + organize field row (#715)

* added decimal scale and precision support

* update i18n

* added button to reset - made values always enabled in pairs

* made button use ml

* added fix for when manually defined scales are set to 0

* fix

* some fixes

* some fixes

* some fixes

* some fixes

* some fixes

---------

Co-authored-by: Alexander Harris <mcalapurge@techie.com>
Co-authored-by: Guy Ben-Aharon <baguy3@gmail.com>
This commit is contained in:
Jonathan Fishner
2025-07-24 15:18:33 +03:00
committed by GitHub
parent fb92be7d3e
commit 778f85d492
42 changed files with 716 additions and 98 deletions

View File

@@ -227,7 +227,7 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
onSelect={() =>
handleSelect(
option.value,
matches?.map((match) => match.toString())
matches?.map((match) => match?.toString())
)
}
>

View File

@@ -19,6 +19,9 @@ export interface LocalConfigContext {
showCardinality: boolean;
setShowCardinality: (showCardinality: boolean) => void;
showFieldAttributes: boolean;
setShowFieldAttributes: (showFieldAttributes: boolean) => void;
hideMultiSchemaNotification: boolean;
setHideMultiSchemaNotification: (
hideMultiSchemaNotification: boolean
@@ -50,6 +53,9 @@ export const LocalConfigContext = createContext<LocalConfigContext>({
showCardinality: true,
setShowCardinality: emptyFn,
showFieldAttributes: true,
setShowFieldAttributes: emptyFn,
hideMultiSchemaNotification: false,
setHideMultiSchemaNotification: emptyFn,

View File

@@ -7,6 +7,7 @@ const themeKey = 'theme';
const scrollActionKey = 'scroll_action';
const schemasFilterKey = 'schemas_filter';
const showCardinalityKey = 'show_cardinality';
const showFieldAttributesKey = 'show_field_attributes';
const hideMultiSchemaNotificationKey = 'hide_multi_schema_notification';
const githubRepoOpenedKey = 'github_repo_opened';
const starUsDialogLastOpenKey = 'star_us_dialog_last_open';
@@ -34,6 +35,11 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
(localStorage.getItem(showCardinalityKey) || 'true') === 'true'
);
const [showFieldAttributes, setShowFieldAttributes] =
React.useState<boolean>(
(localStorage.getItem(showFieldAttributesKey) || 'true') === 'true'
);
const [hideMultiSchemaNotification, setHideMultiSchemaNotification] =
React.useState<boolean>(
(localStorage.getItem(hideMultiSchemaNotificationKey) ||
@@ -119,6 +125,8 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
setSchemasFilter,
showCardinality,
setShowCardinality,
showFieldAttributes,
setShowFieldAttributes,
hideMultiSchemaNotification,
setHideMultiSchemaNotification,
setGithubRepoOpened,

View File

@@ -26,6 +26,8 @@ export const ar: LanguageTranslation = {
hide_sidebar: 'إخفاء الشريط الجانبي',
hide_cardinality: 'إخفاء الكاردينالية',
show_cardinality: 'إظهار الكاردينالية',
hide_field_attributes: 'إخفاء خصائص الحقل',
show_field_attributes: 'إظهار خصائص الحقل',
zoom_on_scroll: 'تكبير/تصغير عند التمرير',
theme: 'المظهر',
show_dependencies: 'إظهار الاعتمادات',
@@ -151,6 +153,8 @@ export const ar: LanguageTranslation = {
delete_field: 'حذف الحقل',
// TODO: Translate
character_length: 'Max Length',
precision: 'الدقة',
scale: 'النطاق',
default_value: 'Default Value',
no_default: 'No default',
},

View File

@@ -26,6 +26,8 @@ export const bn: LanguageTranslation = {
hide_sidebar: 'সাইডবার লুকান',
hide_cardinality: 'কার্ডিনালিটি লুকান',
show_cardinality: 'কার্ডিনালিটি দেখান',
hide_field_attributes: 'ফিল্ড অ্যাট্রিবিউট লুকান',
show_field_attributes: 'ফিল্ড অ্যাট্রিবিউট দেখান',
zoom_on_scroll: 'স্ক্রলে জুম করুন',
theme: 'থিম',
show_dependencies: 'নির্ভরতাগুলি দেখান',
@@ -155,6 +157,8 @@ export const bn: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'নির্ভুলতা',
scale: 'স্কেল',
},
index_actions: {
title: 'ইনডেক্স কর্ম',

View File

@@ -26,6 +26,8 @@ export const de: LanguageTranslation = {
hide_sidebar: 'Seitenleiste ausblenden',
hide_cardinality: 'Kardinalität ausblenden',
show_cardinality: 'Kardinalität anzeigen',
hide_field_attributes: 'Feldattribute ausblenden',
show_field_attributes: 'Feldattribute anzeigen',
zoom_on_scroll: 'Zoom beim Scrollen',
theme: 'Stil',
show_dependencies: 'Abhängigkeiten anzeigen',
@@ -156,6 +158,8 @@ export const de: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'Präzision',
scale: 'Skalierung',
},
index_actions: {
title: 'Indexattribute',

View File

@@ -26,6 +26,8 @@ export const en = {
hide_sidebar: 'Hide Sidebar',
hide_cardinality: 'Hide Cardinality',
show_cardinality: 'Show Cardinality',
hide_field_attributes: 'Hide Field Attributes',
show_field_attributes: 'Show Field Attributes',
zoom_on_scroll: 'Zoom on Scroll',
theme: 'Theme',
show_dependencies: 'Show Dependencies',
@@ -143,6 +145,8 @@ export const en = {
title: 'Field Attributes',
unique: 'Unique',
character_length: 'Max Length',
precision: 'Precision',
scale: 'Scale',
comments: 'Comments',
no_comments: 'No comments',
default_value: 'Default Value',

View File

@@ -24,6 +24,8 @@ export const es: LanguageTranslation = {
view: 'Ver',
hide_cardinality: 'Ocultar Cardinalidad',
show_cardinality: 'Mostrar Cardinalidad',
show_field_attributes: 'Mostrar Atributos de Campo',
hide_field_attributes: 'Ocultar Atributos de Campo',
show_sidebar: 'Mostrar Barra Lateral',
hide_sidebar: 'Ocultar Barra Lateral',
zoom_on_scroll: 'Zoom al Desplazarse',
@@ -145,6 +147,8 @@ export const es: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'Precisión',
scale: 'Escala',
},
index_actions: {
title: 'Atributos del Índice',

View File

@@ -26,6 +26,8 @@ export const fr: LanguageTranslation = {
hide_sidebar: 'Cacher la Barre Latérale',
hide_cardinality: 'Cacher la Cardinalité',
show_cardinality: 'Afficher la Cardinalité',
hide_field_attributes: 'Masquer les Attributs de Champ',
show_field_attributes: 'Afficher les Attributs de Champ',
zoom_on_scroll: 'Zoom sur le Défilement',
theme: 'Thème',
show_dependencies: 'Afficher les Dépendances',
@@ -143,6 +145,8 @@ export const fr: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'Précision',
scale: 'Échelle',
},
index_actions: {
title: "Attributs de l'Index",

View File

@@ -26,6 +26,8 @@ export const gu: LanguageTranslation = {
hide_sidebar: 'સાઇડબાર છુપાવો',
hide_cardinality: 'કાર્ડિનાલિટી છુપાવો',
show_cardinality: 'કાર્ડિનાલિટી બતાવો',
hide_field_attributes: 'ફીલ્ડ અટ્રિબ્યુટ્સ છુપાવો',
show_field_attributes: 'ફીલ્ડ અટ્રિબ્યુટ્સ બતાવો',
zoom_on_scroll: 'સ્ક્રોલ પર ઝૂમ કરો',
theme: 'થિમ',
show_dependencies: 'નિર્ભરતાઓ બતાવો',
@@ -156,6 +158,8 @@ export const gu: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'ચોકસાઈ',
scale: 'માપ',
},
index_actions: {
title: 'ઇન્ડેક્સ લક્ષણો',

View File

@@ -26,6 +26,8 @@ export const hi: LanguageTranslation = {
hide_sidebar: 'साइडबार छिपाएँ',
hide_cardinality: 'कार्डिनैलिटी छिपाएँ',
show_cardinality: 'कार्डिनैलिटी दिखाएँ',
hide_field_attributes: 'फ़ील्ड विशेषताएँ छिपाएँ',
show_field_attributes: 'फ़ील्ड विशेषताएँ दिखाएँ',
zoom_on_scroll: 'स्क्रॉल पर ज़ूम',
theme: 'थीम',
show_dependencies: 'निर्भरता दिखाएँ',
@@ -155,6 +157,8 @@ export const hi: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'Precision',
scale: 'Scale',
},
index_actions: {
title: 'सूचकांक विशेषताएँ',

View File

@@ -26,6 +26,8 @@ export const id_ID: LanguageTranslation = {
hide_sidebar: 'Sembunyikan Sidebar',
hide_cardinality: 'Sembunyikan Kardinalitas',
show_cardinality: 'Tampilkan Kardinalitas',
hide_field_attributes: 'Sembunyikan Atribut Kolom',
show_field_attributes: 'Tampilkan Atribut Kolom',
zoom_on_scroll: 'Perbesar saat Scroll',
theme: 'Tema',
show_dependencies: 'Tampilkan Dependensi',
@@ -154,6 +156,8 @@ export const id_ID: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'Presisi',
scale: 'Skala',
},
index_actions: {
title: 'Atribut Indeks',

View File

@@ -26,6 +26,8 @@ export const ja: LanguageTranslation = {
hide_sidebar: 'サイドバーを非表示',
hide_cardinality: 'カーディナリティを非表示',
show_cardinality: 'カーディナリティを表示',
hide_field_attributes: 'フィールド属性を非表示',
show_field_attributes: 'フィールド属性を表示',
zoom_on_scroll: 'スクロールでズーム',
theme: 'テーマ',
// TODO: Translate
@@ -158,6 +160,8 @@ export const ja: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: '精度',
scale: '小数点以下桁数',
},
index_actions: {
title: 'インデックス属性',

View File

@@ -26,6 +26,8 @@ export const ko_KR: LanguageTranslation = {
hide_sidebar: '사이드바 숨기기',
hide_cardinality: '카디널리티 숨기기',
show_cardinality: '카디널리티 보이기',
hide_field_attributes: '필드 속성 숨기기',
show_field_attributes: '필드 속성 보이기',
zoom_on_scroll: '스크롤 시 확대',
theme: '테마',
show_dependencies: '종속성 보이기',
@@ -154,6 +156,8 @@ export const ko_KR: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: '정밀도',
scale: '소수점 자릿수',
},
index_actions: {
title: '인덱스 속성',

View File

@@ -26,6 +26,8 @@ export const mr: LanguageTranslation = {
hide_sidebar: 'साइडबार लपवा',
hide_cardinality: 'कार्डिनॅलिटी लपवा',
show_cardinality: 'कार्डिनॅलिटी दाखवा',
hide_field_attributes: 'फील्ड गुणधर्म लपवा',
show_field_attributes: 'फील्ड गुणधर्म दाखवा',
zoom_on_scroll: 'स्क्रोलवर झूम करा',
theme: 'थीम',
show_dependencies: 'डिपेंडेन्सि दाखवा',
@@ -157,6 +159,8 @@ export const mr: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'अचूकता',
scale: 'प्रमाण',
},
index_actions: {
title: 'इंडेक्स गुणधर्म',

View File

@@ -26,6 +26,8 @@ export const ne: LanguageTranslation = {
hide_sidebar: 'साइडबार लुकाउनुहोस्',
hide_cardinality: 'कार्डिन्यालिटी लुकाउनुहोस्',
show_cardinality: 'कार्डिन्यालिटी देखाउनुहोस्',
hide_field_attributes: 'फिल्ड विशेषताहरू लुकाउनुहोस्',
show_field_attributes: 'फिल्ड विशेषताहरू देखाउनुहोस्',
zoom_on_scroll: 'स्क्रोलमा जुम गर्नुहोस्',
theme: 'थिम',
show_dependencies: 'डिपेन्डेन्सीहरू देखाउनुहोस्',
@@ -155,6 +157,8 @@ export const ne: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'परिशुद्धता',
scale: 'स्केल',
},
index_actions: {
title: 'सूचक विशेषताहरू',

View File

@@ -26,6 +26,8 @@ export const pt_BR: LanguageTranslation = {
hide_sidebar: 'Ocultar Barra Lateral',
hide_cardinality: 'Ocultar Cardinalidade',
show_cardinality: 'Mostrar Cardinalidade',
hide_field_attributes: 'Ocultar Atributos de Campo',
show_field_attributes: 'Mostrar Atributos de Campo',
zoom_on_scroll: 'Zoom ao Rolar',
theme: 'Tema',
show_dependencies: 'Mostrar Dependências',
@@ -155,6 +157,8 @@ export const pt_BR: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'Precisão',
scale: 'Escala',
},
index_actions: {
title: 'Atributos do Índice',

View File

@@ -26,6 +26,8 @@ export const ru: LanguageTranslation = {
hide_sidebar: 'Скрыть боковую панель',
hide_cardinality: 'Скрыть виды связи',
show_cardinality: 'Показать виды связи',
show_field_attributes: 'Показать атрибуты поля',
hide_field_attributes: 'Скрыть атрибуты поля',
zoom_on_scroll: 'Увеличение при прокрутке',
theme: 'Тема',
show_dependencies: 'Показать зависимости',
@@ -151,6 +153,8 @@ export const ru: LanguageTranslation = {
default_value: 'Default Value',
no_default: 'No default',
character_length: 'Макс. длина',
precision: 'Точность',
scale: 'Масштаб',
},
index_actions: {
title: 'Атрибуты индекса',

View File

@@ -26,6 +26,8 @@ export const te: LanguageTranslation = {
hide_sidebar: 'సైడ్‌బార్ దాచండి',
hide_cardinality: 'కార్డినాలిటీని దాచండి',
show_cardinality: 'కార్డినాలిటీని చూపించండి',
show_field_attributes: 'ఫీల్డ్ గుణాలను చూపించు',
hide_field_attributes: 'ఫీల్డ్ గుణాలను దాచండి',
zoom_on_scroll: 'స్క్రోల్‌పై జూమ్',
theme: 'థీమ్',
show_dependencies: 'ఆధారాలు చూపించండి',
@@ -155,6 +157,8 @@ export const te: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'సూక్ష్మత',
scale: 'స్కేల్',
},
index_actions: {
title: 'ఇండెక్స్ గుణాలు',

View File

@@ -26,6 +26,8 @@ export const tr: LanguageTranslation = {
hide_sidebar: 'Kenar Çubuğunu Gizle',
hide_cardinality: 'Kardinaliteyi Gizle',
show_cardinality: 'Kardinaliteyi Göster',
show_field_attributes: 'Alan Özelliklerini Göster',
hide_field_attributes: 'Alan Özelliklerini Gizle',
zoom_on_scroll: 'Kaydırarak Yakınlaştır',
theme: 'Tema',
show_dependencies: 'Bağımlılıkları Göster',
@@ -154,6 +156,8 @@ export const tr: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'Hassasiyet',
scale: 'Ölçek',
},
index_actions: {
title: 'İndeks Özellikleri',

View File

@@ -26,6 +26,8 @@ export const uk: LanguageTranslation = {
hide_sidebar: 'Приховати бічну панель',
hide_cardinality: 'Приховати потужність',
show_cardinality: 'Показати кардинальність',
show_field_attributes: 'Показати атрибути полів',
hide_field_attributes: 'Приховати атрибути полів',
zoom_on_scroll: 'Масштабувати прокручуванням',
theme: 'Тема',
show_dependencies: 'Показати залежності',
@@ -153,6 +155,8 @@ export const uk: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'Точність',
scale: 'Масштаб',
},
index_actions: {
title: 'Атрибути індексу',

View File

@@ -26,6 +26,8 @@ export const vi: LanguageTranslation = {
hide_sidebar: 'Ẩn thanh bên',
hide_cardinality: 'Ẩn số lượng',
show_cardinality: 'Hiển thị số lượng',
show_field_attributes: 'Hiển thị thuộc tính trường',
hide_field_attributes: 'Ẩn thuộc tính trường',
zoom_on_scroll: 'Thu phóng khi cuộn',
theme: 'Chủ đề',
show_dependencies: 'Hiển thị các phụ thuộc',
@@ -154,6 +156,8 @@ export const vi: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: 'Độ chính xác',
scale: 'Tỷ lệ',
},
index_actions: {
title: 'Thuộc tính chỉ mục',

View File

@@ -26,6 +26,8 @@ export const zh_CN: LanguageTranslation = {
hide_sidebar: '隐藏侧边栏',
hide_cardinality: '隐藏基数',
show_cardinality: '展示基数',
show_field_attributes: '展示字段属性',
hide_field_attributes: '隐藏字段属性',
zoom_on_scroll: '滚动缩放',
theme: '主题',
show_dependencies: '展示依赖',
@@ -151,6 +153,8 @@ export const zh_CN: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: '精度',
scale: '小数位',
},
index_actions: {
title: '索引属性',

View File

@@ -26,6 +26,8 @@ export const zh_TW: LanguageTranslation = {
hide_sidebar: '隱藏側邊欄',
hide_cardinality: '隱藏基數',
show_cardinality: '顯示基數',
hide_field_attributes: '隱藏欄位屬性',
show_field_attributes: '顯示欄位屬性',
zoom_on_scroll: '滾動縮放',
theme: '主題',
show_dependencies: '顯示相依性',
@@ -151,6 +153,8 @@ export const zh_TW: LanguageTranslation = {
no_default: 'No default',
// TODO: Translate
character_length: 'Max Length',
precision: '精度',
scale: '小數位',
},
index_actions: {
title: '索引屬性',

View File

@@ -48,18 +48,30 @@ export const clickhouseDataTypes: readonly DataTypeData[] = [
{ name: 'mediumblob', id: 'mediumblob' },
{ name: 'tinyblob', id: 'tinyblob' },
{ name: 'blob', id: 'blob' },
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true },
{ name: 'char', id: 'char', hasCharMaxLength: true },
{
name: 'varchar',
id: 'varchar',
fieldAttributes: { hasCharMaxLength: true },
},
{ name: 'char', id: 'char', fieldAttributes: { hasCharMaxLength: true } },
{ name: 'char large object', id: 'char_large_object' },
{ name: 'char varying', id: 'char_varying', hasCharMaxLength: true },
{
name: 'char varying',
id: 'char_varying',
fieldAttributes: { hasCharMaxLength: true },
},
{ name: 'character large object', id: 'character_large_object' },
{
name: 'character varying',
id: 'character_varying',
hasCharMaxLength: true,
fieldAttributes: { hasCharMaxLength: true },
},
{ name: 'nchar large object', id: 'nchar_large_object' },
{ name: 'nchar varying', id: 'nchar_varying', hasCharMaxLength: true },
{
name: 'nchar varying',
id: 'nchar_varying',
fieldAttributes: { hasCharMaxLength: true },
},
{
name: 'national character large object',
id: 'national_character_large_object',
@@ -67,22 +79,34 @@ export const clickhouseDataTypes: readonly DataTypeData[] = [
{
name: 'national character varying',
id: 'national_character_varying',
hasCharMaxLength: true,
fieldAttributes: { hasCharMaxLength: true },
},
{
name: 'national char varying',
id: 'national_char_varying',
hasCharMaxLength: true,
fieldAttributes: { hasCharMaxLength: true },
},
{
name: 'national character',
id: 'national_character',
hasCharMaxLength: true,
fieldAttributes: { hasCharMaxLength: true },
},
{
name: 'national char',
id: 'national_char',
fieldAttributes: { hasCharMaxLength: true },
},
{ name: 'national char', id: 'national_char', hasCharMaxLength: true },
{ name: 'binary large object', id: 'binary_large_object' },
{ name: 'binary varying', id: 'binary_varying', hasCharMaxLength: true },
{ name: 'fixedstring', id: 'fixedstring', hasCharMaxLength: true },
{
name: 'binary varying',
id: 'binary_varying',
fieldAttributes: { hasCharMaxLength: true },
},
{
name: 'fixedstring',
id: 'fixedstring',
fieldAttributes: { hasCharMaxLength: true },
},
{ name: 'string', id: 'string' },
// Date Types

View File

@@ -14,9 +14,21 @@ export interface DataType {
name: string;
}
export interface DataTypeData extends DataType {
export interface FieldAttributeRange {
max: number;
min: number;
default: number;
}
interface FieldAttributes {
hasCharMaxLength?: boolean;
precision?: FieldAttributeRange;
scale?: FieldAttributeRange;
}
export interface DataTypeData extends DataType {
usageLevel?: 1 | 2; // Level 1 is most common, Level 2 is second most common
fieldAttributes?: FieldAttributes;
}
export const dataTypeSchema: z.ZodType<DataType> = z.object({

View File

@@ -2,7 +2,12 @@ import type { DataTypeData } from './data-types';
export const genericDataTypes: readonly DataTypeData[] = [
// Level 1 - Most commonly used types
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true, usageLevel: 1 },
{
name: 'varchar',
id: 'varchar',
fieldAttributes: { hasCharMaxLength: true },
usageLevel: 1,
},
{ name: 'int', id: 'int', usageLevel: 1 },
{ name: 'text', id: 'text', usageLevel: 1 },
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
@@ -10,23 +15,62 @@ export const genericDataTypes: readonly DataTypeData[] = [
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
// Level 2 - Second most common types
{ name: 'decimal', id: 'decimal', usageLevel: 2 },
{
name: 'decimal',
id: 'decimal',
usageLevel: 2,
fieldAttributes: {
precision: {
max: 999,
min: 1,
default: 10,
},
scale: {
max: 999,
min: 0,
default: 2,
},
},
},
{ name: 'datetime', id: 'datetime', usageLevel: 2 },
{ name: 'json', id: 'json', usageLevel: 2 },
{ name: 'uuid', id: 'uuid', usageLevel: 2 },
// Less common types
{ name: 'bigint', id: 'bigint' },
{ name: 'binary', id: 'binary', hasCharMaxLength: true },
{
name: 'binary',
id: 'binary',
fieldAttributes: { hasCharMaxLength: true },
},
{ name: 'blob', id: 'blob' },
{ name: 'char', id: 'char', hasCharMaxLength: true },
{ name: 'char', id: 'char', fieldAttributes: { hasCharMaxLength: true } },
{ name: 'double', id: 'double' },
{ name: 'enum', id: 'enum' },
{ name: 'float', id: 'float' },
{ name: 'numeric', id: 'numeric' },
{
name: 'numeric',
id: 'numeric',
fieldAttributes: {
precision: {
max: 999,
min: 1,
default: 10,
},
scale: {
max: 999,
min: 0,
default: 2,
},
},
},
{ name: 'real', id: 'real' },
{ name: 'set', id: 'set' },
{ name: 'smallint', id: 'smallint' },
{ name: 'time', id: 'time' },
{ name: 'varbinary', id: 'varbinary', hasCharMaxLength: true },
{
name: 'varbinary',
id: 'varbinary',
fieldAttributes: { hasCharMaxLength: true },
},
] as const;

View File

@@ -4,12 +4,32 @@ export const mariadbDataTypes: readonly DataTypeData[] = [
// Level 1 - Most commonly used types
{ name: 'int', id: 'int', usageLevel: 1 },
{ name: 'bigint', id: 'bigint', usageLevel: 1 },
{ name: 'decimal', id: 'decimal', usageLevel: 1 },
{
name: 'decimal',
id: 'decimal',
usageLevel: 1,
fieldAttributes: {
precision: {
max: 65,
min: 1,
default: 10,
},
scale: {
max: 30,
min: 0,
default: 0,
},
},
},
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
{ name: 'datetime', id: 'datetime', usageLevel: 1 },
{ name: 'date', id: 'date', usageLevel: 1 },
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true, usageLevel: 1 },
{
name: 'varchar',
id: 'varchar',
fieldAttributes: { hasCharMaxLength: true },
},
{ name: 'text', id: 'text', usageLevel: 1 },
// Level 2 - Second most common types
@@ -20,16 +40,39 @@ export const mariadbDataTypes: readonly DataTypeData[] = [
{ name: 'tinyint', id: 'tinyint' },
{ name: 'smallint', id: 'smallint' },
{ name: 'mediumint', id: 'mediumint' },
{ name: 'numeric', id: 'numeric' },
{
name: 'numeric',
id: 'numeric',
fieldAttributes: {
precision: {
max: 65,
min: 1,
default: 10,
},
scale: {
max: 30,
min: 0,
default: 0,
},
},
},
{ name: 'float', id: 'float' },
{ name: 'double', id: 'double' },
{ name: 'bit', id: 'bit' },
{ name: 'bool', id: 'bool' },
{ name: 'time', id: 'time' },
{ name: 'year', id: 'year' },
{ name: 'char', id: 'char', hasCharMaxLength: true },
{ name: 'binary', id: 'binary', hasCharMaxLength: true },
{ name: 'varbinary', id: 'varbinary', hasCharMaxLength: true },
{ name: 'char', id: 'char', fieldAttributes: { hasCharMaxLength: true } },
{
name: 'binary',
id: 'binary',
fieldAttributes: { hasCharMaxLength: true },
},
{
name: 'varbinary',
id: 'varbinary',
fieldAttributes: { hasCharMaxLength: true },
},
{ name: 'tinyblob', id: 'tinyblob' },
{ name: 'blob', id: 'blob' },
{ name: 'mediumblob', id: 'mediumblob' },

View File

@@ -3,7 +3,12 @@ import type { DataTypeData } from './data-types';
export const mysqlDataTypes: readonly DataTypeData[] = [
// Level 1 - Most commonly used types
{ name: 'int', id: 'int', usageLevel: 1 },
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true, usageLevel: 1 },
{
name: 'varchar',
id: 'varchar',
fieldAttributes: { hasCharMaxLength: true },
usageLevel: 1,
},
{ name: 'text', id: 'text', usageLevel: 1 },
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
@@ -11,7 +16,23 @@ export const mysqlDataTypes: readonly DataTypeData[] = [
// Level 2 - Second most common types
{ name: 'bigint', id: 'bigint', usageLevel: 2 },
{ name: 'decimal', id: 'decimal', usageLevel: 2 },
{
name: 'decimal',
id: 'decimal',
usageLevel: 2,
fieldAttributes: {
precision: {
max: 65,
min: 1,
default: 10,
},
scale: {
max: 30,
min: 0,
default: 0,
},
},
},
{ name: 'datetime', id: 'datetime', usageLevel: 2 },
{ name: 'json', id: 'json', usageLevel: 2 },
@@ -22,7 +43,7 @@ export const mysqlDataTypes: readonly DataTypeData[] = [
{ name: 'float', id: 'float' },
{ name: 'double', id: 'double' },
{ name: 'bit', id: 'bit' },
{ name: 'char', id: 'char', hasCharMaxLength: true },
{ name: 'char', id: 'char', fieldAttributes: { hasCharMaxLength: true } },
{ name: 'tinytext', id: 'tinytext' },
{ name: 'mediumtext', id: 'mediumtext' },
{ name: 'longtext', id: 'longtext' },

View File

@@ -2,15 +2,30 @@ import type { DataTypeData } from './data-types';
export const oracleDataTypes: readonly DataTypeData[] = [
// Character types
{ name: 'VARCHAR2', id: 'varchar2', usageLevel: 1, hasCharMaxLength: true },
{
name: 'VARCHAR2',
id: 'varchar2',
usageLevel: 1,
fieldAttributes: { hasCharMaxLength: true },
},
{
name: 'NVARCHAR2',
id: 'nvarchar2',
usageLevel: 1,
hasCharMaxLength: true,
fieldAttributes: { hasCharMaxLength: true },
},
{
name: 'CHAR',
id: 'char',
usageLevel: 2,
fieldAttributes: { hasCharMaxLength: true },
},
{
name: 'NCHAR',
id: 'nchar',
usageLevel: 2,
fieldAttributes: { hasCharMaxLength: true },
},
{ name: 'CHAR', id: 'char', usageLevel: 2, hasCharMaxLength: true },
{ name: 'NCHAR', id: 'nchar', usageLevel: 2, hasCharMaxLength: true },
{ name: 'CLOB', id: 'clob', usageLevel: 2 },
{ name: 'NCLOB', id: 'nclob', usageLevel: 2 },
@@ -49,7 +64,12 @@ export const oracleDataTypes: readonly DataTypeData[] = [
{ name: 'BFILE', id: 'bfile', usageLevel: 2 },
// Other types
{ name: 'RAW', id: 'raw', usageLevel: 2, hasCharMaxLength: true },
{
name: 'RAW',
id: 'raw',
usageLevel: 2,
fieldAttributes: { hasCharMaxLength: true },
},
{ name: 'LONG RAW', id: 'long_raw', usageLevel: 2 },
{ name: 'ROWID', id: 'rowid', usageLevel: 2 },
{ name: 'UROWID', id: 'urowid', usageLevel: 2 },

View File

@@ -3,7 +3,12 @@ import type { DataTypeData } from './data-types';
export const postgresDataTypes: readonly DataTypeData[] = [
// Level 1 - Most commonly used types
{ name: 'integer', id: 'integer', usageLevel: 1 },
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true, usageLevel: 1 },
{
name: 'varchar',
id: 'varchar',
fieldAttributes: { hasCharMaxLength: true },
usageLevel: 1,
},
{ name: 'text', id: 'text', usageLevel: 1 },
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
@@ -11,7 +16,23 @@ export const postgresDataTypes: readonly DataTypeData[] = [
// Level 2 - Second most common types
{ name: 'bigint', id: 'bigint', usageLevel: 2 },
{ name: 'decimal', id: 'decimal', usageLevel: 2 },
{
name: 'decimal',
id: 'decimal',
usageLevel: 2,
fieldAttributes: {
precision: {
max: 131072,
min: 0,
default: 10,
},
scale: {
max: 16383,
min: 0,
default: 2,
},
},
},
{ name: 'serial', id: 'serial', usageLevel: 2 },
{ name: 'json', id: 'json', usageLevel: 2 },
{ name: 'jsonb', id: 'jsonb', usageLevel: 2 },
@@ -23,18 +44,33 @@ export const postgresDataTypes: readonly DataTypeData[] = [
},
// Less common types
{ name: 'numeric', id: 'numeric' },
{
name: 'numeric',
id: 'numeric',
fieldAttributes: {
precision: {
max: 131072,
min: 0,
default: 10,
},
scale: {
max: 16383,
min: 0,
default: 2,
},
},
},
{ name: 'real', id: 'real' },
{ name: 'double precision', id: 'double_precision' },
{ name: 'smallserial', id: 'smallserial' },
{ name: 'bigserial', id: 'bigserial' },
{ name: 'money', id: 'money' },
{ name: 'smallint', id: 'smallint' },
{ name: 'char', id: 'char', hasCharMaxLength: true },
{ name: 'char', id: 'char', fieldAttributes: { hasCharMaxLength: true } },
{
name: 'character varying',
id: 'character_varying',
hasCharMaxLength: true,
fieldAttributes: { hasCharMaxLength: true },
},
{ name: 'time', id: 'time' },
{ name: 'timestamp without time zone', id: 'timestamp_without_time_zone' },

View File

@@ -4,32 +4,81 @@ export const sqlServerDataTypes: readonly DataTypeData[] = [
// Level 1 - Most commonly used types
{ name: 'int', id: 'int', usageLevel: 1 },
{ name: 'bit', id: 'bit', usageLevel: 1 },
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true, usageLevel: 1 },
{ name: 'nvarchar', id: 'nvarchar', hasCharMaxLength: true, usageLevel: 1 },
{
name: 'varchar',
id: 'varchar',
fieldAttributes: { hasCharMaxLength: true },
usageLevel: 1,
},
{
name: 'nvarchar',
id: 'nvarchar',
fieldAttributes: { hasCharMaxLength: true },
usageLevel: 1,
},
{ name: 'text', id: 'text', usageLevel: 1 },
{ name: 'datetime', id: 'datetime', usageLevel: 1 },
{ name: 'date', id: 'date', usageLevel: 1 },
// Level 2 - Second most common types
{ name: 'bigint', id: 'bigint', usageLevel: 2 },
{ name: 'decimal', id: 'decimal', usageLevel: 2 },
{
name: 'decimal',
id: 'decimal',
usageLevel: 2,
fieldAttributes: {
precision: {
max: 38,
min: 1,
default: 18,
},
scale: {
max: 38,
min: 0,
default: 0,
},
},
},
{ name: 'datetime2', id: 'datetime2', usageLevel: 2 },
{ name: 'uniqueidentifier', id: 'uniqueidentifier', usageLevel: 2 },
{ name: 'json', id: 'json', usageLevel: 2 },
// Less common types
{ name: 'numeric', id: 'numeric' },
{
name: 'numeric',
id: 'numeric',
fieldAttributes: {
precision: {
max: 38,
min: 1,
default: 18,
},
scale: {
max: 38,
min: 0,
default: 0,
},
},
},
{ name: 'smallint', id: 'smallint' },
{ name: 'smallmoney', id: 'smallmoney' },
{ name: 'tinyint', id: 'tinyint' },
{ name: 'money', id: 'money' },
{ name: 'float', id: 'float' },
{ name: 'real', id: 'real' },
{ name: 'char', id: 'char', hasCharMaxLength: true },
{ name: 'nchar', id: 'nchar', hasCharMaxLength: true },
{ name: 'char', id: 'char', fieldAttributes: { hasCharMaxLength: true } },
{ name: 'nchar', id: 'nchar', fieldAttributes: { hasCharMaxLength: true } },
{ name: 'ntext', id: 'ntext' },
{ name: 'binary', id: 'binary', hasCharMaxLength: true },
{ name: 'varbinary', id: 'varbinary', hasCharMaxLength: true },
{
name: 'binary',
id: 'binary',
fieldAttributes: { hasCharMaxLength: true },
},
{
name: 'varbinary',
id: 'varbinary',
fieldAttributes: { hasCharMaxLength: true },
},
{ name: 'image', id: 'image' },
{ name: 'datetimeoffset', id: 'datetimeoffset' },
{ name: 'smalldatetime', id: 'smalldatetime' },

View File

@@ -10,21 +10,41 @@ export const sqliteDataTypes: readonly DataTypeData[] = [
// SQLite type aliases and common types
{ name: 'int', id: 'int', usageLevel: 1 },
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true, usageLevel: 1 },
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
{ name: 'date', id: 'date', usageLevel: 1 },
{ name: 'datetime', id: 'datetime', usageLevel: 1 },
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
{
name: 'varchar',
id: 'varchar',
fieldAttributes: {
hasCharMaxLength: true,
},
usageLevel: 1,
},
{
name: 'timestamp',
id: 'timestamp',
usageLevel: 1,
},
// Level 2 - Second most common types
{ name: 'numeric', id: 'numeric', usageLevel: 2 },
{ name: 'decimal', id: 'decimal', usageLevel: 2 },
{ name: 'float', id: 'float', usageLevel: 2 },
{
name: 'decimal',
id: 'decimal',
usageLevel: 2,
},
{ name: 'double', id: 'double', usageLevel: 2 },
{ name: 'json', id: 'json', usageLevel: 2 },
// Less common types (all map to SQLite storage classes)
{ name: 'char', id: 'char', hasCharMaxLength: true },
{
name: 'char',
id: 'char',
fieldAttributes: {
hasCharMaxLength: true,
},
usageLevel: 2,
},
{ name: 'binary', id: 'binary' },
{ name: 'varbinary', id: 'varbinary' },
{ name: 'smallint', id: 'smallint' },

View File

@@ -136,7 +136,8 @@ export function exportMSSQL({
) {
typeWithSize = `${typeName}(${field.characterMaximumLength})`;
}
} else if (field.precision && field.scale) {
}
if (field.precision && field.scale) {
if (
typeName.toLowerCase() === 'decimal' ||
typeName.toLowerCase() === 'numeric'

View File

@@ -244,7 +244,8 @@ export function exportMySQL({
) {
typeWithSize = `${typeName}(${field.characterMaximumLength})`;
}
} else if (field.precision && field.scale) {
}
if (field.precision && field.scale) {
if (
typeName.toLowerCase() === 'decimal' ||
typeName.toLowerCase() === 'numeric'

View File

@@ -269,7 +269,8 @@ export function exportPostgreSQL({
) {
typeWithSize = `${typeName}(${field.characterMaximumLength})`;
}
} else if (field.precision && field.scale) {
}
if (field.precision && field.scale) {
if (
typeName.toLowerCase() === 'decimal' ||
typeName.toLowerCase() === 'numeric'

View File

@@ -254,7 +254,8 @@ export const exportBaseSQL = ({
// Add size for character types
if (
field.characterMaximumLength &&
parseInt(field.characterMaximumLength) > 0
parseInt(field.characterMaximumLength) > 0 &&
field.type.name.toLowerCase() !== 'decimal'
) {
sqlScript += `(${field.characterMaximumLength})`;
} else if (field.type.name.toLowerCase().includes('varchar')) {

View File

@@ -1,10 +1,15 @@
import { z } from 'zod';
import { dataTypeSchema, type DataType } from '../data/data-types/data-types';
import {
dataTypeSchema,
findDataTypeDataById,
type DataType,
} from '../data/data-types/data-types';
import type { ColumnInfo } from '../data/import-metadata/metadata-types/column-info';
import type { AggregatedIndexInfo } from '../data/import-metadata/metadata-types/index-info';
import type { PrimaryKeyInfo } from '../data/import-metadata/metadata-types/primary-key-info';
import type { TableInfo } from '../data/import-metadata/metadata-types/table-info';
import { generateId } from '../utils';
import type { DatabaseType } from './database-type';
export interface DBField {
id: string;
@@ -97,3 +102,80 @@ export const createFieldsFromMetadata = ({
})
);
};
export const generateDBFieldSuffix = (
field: DBField,
{
databaseType,
forceExtended = false,
typeId,
}: {
databaseType?: DatabaseType;
forceExtended?: boolean;
typeId?: string;
} = {}
): string => {
if (databaseType && forceExtended && typeId) {
return generateExtendedSuffix(field, databaseType, typeId);
}
return generateStandardSuffix(field);
};
const generateExtendedSuffix = (
field: DBField,
databaseType: DatabaseType,
typeId: string
): string => {
const type = findDataTypeDataById(typeId, databaseType);
if (!type?.fieldAttributes) {
return '';
}
const { fieldAttributes } = type;
// Character maximum length types (e.g., VARCHAR)
if (fieldAttributes.hasCharMaxLength) {
const maxLength = field.characterMaximumLength ?? 'n';
return `(${maxLength})`;
}
// Precision and scale types (e.g., DECIMAL)
if (fieldAttributes.precision && fieldAttributes.scale) {
return formatPrecisionAndScale(field.precision, field.scale, '(p, s)');
}
// Precision only types (e.g., FLOAT)
if (fieldAttributes.precision) {
const precision = field.precision ?? 'p';
return `(${precision})`;
}
return '';
};
const generateStandardSuffix = (field: DBField): string => {
// Character maximum length
if (field.characterMaximumLength) {
return `(${field.characterMaximumLength})`;
}
return formatPrecisionAndScale(field.precision, field.scale, '');
};
const formatPrecisionAndScale = (
precision: number | null | undefined,
scale: number | null | undefined,
fallback: string
): string => {
if (precision && scale) {
return `(${precision}, ${scale})`;
}
if (precision) {
return `(${precision})`;
}
return fallback;
};

View File

@@ -21,7 +21,7 @@ import {
SquarePlus,
Trash2,
} from 'lucide-react';
import type { DBField } from '@/lib/domain/db-field';
import { generateDBFieldSuffix, type DBField } from '@/lib/domain/db-field';
import { useChartDB } from '@/hooks/use-chartdb';
import { cn } from '@/lib/utils';
import {
@@ -32,6 +32,7 @@ import {
import { useClickAway, useKeyPressEvent } from 'react-use';
import { Input } from '@/components/input/input';
import { useDiff } from '@/context/diff-context/use-diff';
import { useLocalConfig } from '@/hooks/use-local-config';
export const LEFT_HANDLE_ID_PREFIX = 'left_rel_';
export const RIGHT_HANDLE_ID_PREFIX = 'right_rel_';
@@ -59,6 +60,10 @@ const arePropsEqual = (
prevProps.field.unique === nextProps.field.unique &&
prevProps.field.type.id === nextProps.field.type.id &&
prevProps.field.type.name === nextProps.field.type.name &&
prevProps.field.characterMaximumLength ===
nextProps.field.characterMaximumLength &&
prevProps.field.precision === nextProps.field.precision &&
prevProps.field.scale === nextProps.field.scale &&
prevProps.focused === nextProps.focused &&
prevProps.highlighted === nextProps.highlighted &&
prevProps.visible === nextProps.visible &&
@@ -203,6 +208,8 @@ export const TableNodeField: React.FC<TableNodeFieldProps> = React.memo(
setEditMode(true);
}, []);
const { showFieldAttributes } = useLocalConfig();
return (
<div
className={cn(
@@ -394,7 +401,7 @@ export const TableNodeField: React.FC<TableNodeFieldProps> = React.memo(
{fieldDiffChangedType.name.split(' ')[0]}
</>
) : (
field.type.name.split(' ')[0]
`${field.type.name.split(' ')[0]}${showFieldAttributes ? generateDBFieldSuffix(field) : ''}`
)}
{field.nullable ? '?' : ''}
</div>

View File

@@ -1,9 +1,10 @@
import React, { useEffect, useRef, useCallback } from 'react';
import React, { useEffect, useRef, useCallback, useMemo } from 'react';
import { Ellipsis, Trash2 } from 'lucide-react';
import { Input } from '@/components/input/input';
import { Button } from '@/components/button/button';
import { Separator } from '@/components/separator/separator';
import type { DBField } from '@/lib/domain/db-field';
import type { FieldAttributeRange } from '@/lib/data/data-types/data-types';
import { findDataTypeDataById } from '@/lib/data/data-types/data-types';
import {
Popover,
@@ -16,15 +17,18 @@ import { useTranslation } from 'react-i18next';
import { Textarea } from '@/components/textarea/textarea';
import { useDebounce } from '@/hooks/use-debounce';
import equal from 'fast-deep-equal';
import type { DatabaseType } from '@/lib/domain';
export interface TableFieldPopoverProps {
field: DBField;
databaseType: DatabaseType;
updateField: (attrs: Partial<DBField>) => void;
removeField: () => void;
}
export const TableFieldPopover: React.FC<TableFieldPopoverProps> = ({
field,
databaseType,
updateField,
removeField,
}) => {
@@ -52,6 +56,8 @@ export const TableFieldPopover: React.FC<TableFieldPopoverProps> = ({
debouncedUpdateField({
comments: localField.comments,
characterMaximumLength: localField.characterMaximumLength,
precision: localField.precision,
scale: localField.scale,
unique: localField.unique,
default: localField.default,
});
@@ -59,6 +65,11 @@ export const TableFieldPopover: React.FC<TableFieldPopoverProps> = ({
prevFieldRef.current = localField;
}, [localField, debouncedUpdateField, isOpen]);
const dataFieldType = useMemo(
() => findDataTypeDataById(field.type.id, databaseType),
[field.type.id, databaseType]
);
return (
<Popover
open={isOpen}
@@ -123,8 +134,7 @@ export const TableFieldPopover: React.FC<TableFieldPopoverProps> = ({
className="w-full rounded-md bg-muted text-sm"
/>
</div>
{findDataTypeDataById(field.type.id)
?.hasCharMaxLength ? (
{dataFieldType?.fieldAttributes?.hasCharMaxLength ? (
<div className="flex flex-col gap-2">
<Label
htmlFor="width"
@@ -150,6 +160,110 @@ export const TableFieldPopover: React.FC<TableFieldPopoverProps> = ({
/>
</div>
) : null}
{dataFieldType?.fieldAttributes?.precision ||
dataFieldType?.fieldAttributes?.scale ? (
<div className="flex gap-2">
<div className="flex flex-1 flex-col gap-2">
<Label
htmlFor="width"
className="text-subtitle"
>
{t(
'side_panel.tables_section.table.field_actions.precision'
)}
</Label>
<Input
value={localField.precision ?? ''}
type="number"
max={
dataFieldType?.fieldAttributes
?.precision
? (
dataFieldType
?.fieldAttributes
?.precision as FieldAttributeRange
).max
: undefined
}
min={
dataFieldType?.fieldAttributes
?.precision
? (
dataFieldType
?.fieldAttributes
?.precision as FieldAttributeRange
).min
: undefined
}
placeholder={
dataFieldType?.fieldAttributes
?.precision
? `${(dataFieldType?.fieldAttributes?.precision as FieldAttributeRange).default}`
: 'Optional'
}
onChange={(e) =>
setLocalField((current) => ({
...current,
precision: e.target.value
? parseInt(e.target.value)
: undefined,
}))
}
className="w-full rounded-md bg-muted text-sm"
/>
</div>
<div className="flex flex-1 flex-col gap-2">
<Label
htmlFor="width"
className="text-subtitle"
>
{t(
'side_panel.tables_section.table.field_actions.scale'
)}
</Label>
<Input
value={localField.scale ?? ''}
max={
dataFieldType?.fieldAttributes
?.scale
? (
dataFieldType
?.fieldAttributes
?.scale as FieldAttributeRange
).max
: undefined
}
min={
dataFieldType?.fieldAttributes
?.scale
? (
findDataTypeDataById(
field.type.id
)?.fieldAttributes
?.scale as FieldAttributeRange
).min
: undefined
}
placeholder={
dataFieldType?.fieldAttributes
?.scale
? `${(dataFieldType?.fieldAttributes?.scale as FieldAttributeRange).default}`
: 'Optional'
}
type="number"
onChange={(e) =>
setLocalField((current) => ({
...current,
scale: e.target.value
? parseInt(e.target.value)
: undefined,
}))
}
className="w-full rounded-md bg-muted text-sm"
/>
</div>
</div>
) : null}
<div className="flex flex-col gap-2">
<Label htmlFor="width" className="text-subtitle">
{t(

View File

@@ -1,7 +1,7 @@
import React, { useCallback, useMemo } from 'react';
import { GripVertical, KeyRound } from 'lucide-react';
import { Input } from '@/components/input/input';
import type { DBField } from '@/lib/domain/db-field';
import { generateDBFieldSuffix, type DBField } from '@/lib/domain/db-field';
import { useChartDB } from '@/hooks/use-chartdb';
import {
dataTypeDataToDataType,
@@ -46,10 +46,22 @@ export const TableField: React.FC<TableFieldProps> = ({
].map((type) => ({
label: type.name,
value: type.id,
regex: type.hasCharMaxLength
regex: type.fieldAttributes?.hasCharMaxLength
? `^${type.name}\\(\\d+\\)$`
: undefined,
extractRegex: type.hasCharMaxLength ? /\((\d+)\)/ : undefined,
: type.fieldAttributes?.precision && type.fieldAttributes?.scale
? `^${type.name}\\s*\\(\\s*\\d+\\s*(?:,\\s*\\d+\\s*)?\\)$`
: type.fieldAttributes?.precision
? `^${type.name}\\s*\\(\\s*\\d+\\s*\\)$`
: undefined,
extractRegex: type.fieldAttributes?.hasCharMaxLength
? /\((\d+)\)/
: type.fieldAttributes?.precision && type.fieldAttributes?.scale
? new RegExp(
`${type.name}\\s*\\(\\s*(\\d+)\\s*(?:,\\s*(\\d+)\\s*)?\\)`
)
: type.fieldAttributes?.precision
? /\((\d+)\)/
: undefined,
group: customTypes?.length ? 'Standard Types' : undefined,
}));
@@ -83,18 +95,44 @@ export const TableField: React.FC<TableFieldProps> = ({
};
let characterMaximumLength: string | undefined = undefined;
let precision: number | undefined = undefined;
let scale: number | undefined = undefined;
if (regexMatches?.length && dataType?.hasCharMaxLength) {
characterMaximumLength = regexMatches[1];
} else if (
field.characterMaximumLength &&
dataType?.hasCharMaxLength
) {
characterMaximumLength = field.characterMaximumLength;
if (regexMatches?.length) {
if (dataType?.fieldAttributes?.hasCharMaxLength) {
characterMaximumLength = regexMatches[1];
} else if (
dataType?.fieldAttributes?.precision &&
dataType?.fieldAttributes?.scale
) {
precision = parseInt(regexMatches[1]);
scale = regexMatches[2]
? parseInt(regexMatches[2])
: undefined;
} else if (dataType?.fieldAttributes?.precision) {
precision = parseInt(regexMatches[1]);
}
} else {
if (
dataType?.fieldAttributes?.hasCharMaxLength &&
field.characterMaximumLength
) {
characterMaximumLength = field.characterMaximumLength;
}
if (dataType?.fieldAttributes?.precision && field.precision) {
precision = field.precision;
}
if (dataType?.fieldAttributes?.scale && field.scale) {
scale = field.scale;
}
}
updateField({
characterMaximumLength,
precision,
scale,
type: dataTypeDataToDataType(
dataType ?? {
id: value as string,
@@ -103,7 +141,13 @@ export const TableField: React.FC<TableFieldProps> = ({
),
});
},
[updateField, databaseType, field.characterMaximumLength]
[
updateField,
databaseType,
field.characterMaximumLength,
field.precision,
field.scale,
]
);
const style = {
@@ -113,12 +157,12 @@ export const TableField: React.FC<TableFieldProps> = ({
return (
<div
className="flex flex-1 touch-none flex-row justify-between p-1"
className="flex flex-1 touch-none flex-row justify-between gap-2 p-1"
ref={setNodeRef}
style={style}
{...attributes}
>
<div className="flex w-8/12 items-center justify-start gap-1 overflow-hidden">
<div className="flex flex-1 items-center justify-start gap-1 overflow-hidden">
<div
className="flex w-4 shrink-0 cursor-move items-center justify-center"
{...listeners}
@@ -127,7 +171,7 @@ export const TableField: React.FC<TableFieldProps> = ({
</div>
<Tooltip>
<TooltipTrigger asChild>
<span className="w-5/12">
<span className="min-w-0 flex-1">
<Input
className="h-8 w-full !truncate focus-visible:ring-0"
type="text"
@@ -146,7 +190,7 @@ export const TableField: React.FC<TableFieldProps> = ({
<TooltipContent>{field.name}</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger className="flex h-8 !w-5/12" asChild>
<TooltipTrigger className="flex h-8 min-w-0 flex-1" asChild>
<span>
<SelectBox
className="flex h-8 min-h-8 w-full"
@@ -156,26 +200,14 @@ export const TableField: React.FC<TableFieldProps> = ({
'side_panel.tables_section.table.field_type'
)}
value={field.type.id}
valueSuffix={
field.characterMaximumLength
? `(${field.characterMaximumLength})`
: ''
valueSuffix={generateDBFieldSuffix(field)}
optionSuffix={(option) =>
generateDBFieldSuffix(field, {
databaseType,
forceExtended: true,
typeId: option.value,
})
}
optionSuffix={(option) => {
const type = sortedDataTypeMap[
databaseType
].find((v) => v.id === option.value);
if (!type) {
return '';
}
if (type.hasCharMaxLength) {
return `(${!field.characterMaximumLength ? 'n' : field.characterMaximumLength})`;
}
return '';
}}
onChange={onChangeDataType}
emptyPlaceholder={t(
'side_panel.tables_section.table.no_types_found'
@@ -191,7 +223,7 @@ export const TableField: React.FC<TableFieldProps> = ({
</TooltipContent>
</Tooltip>
</div>
<div className="flex w-4/12 justify-end gap-1 overflow-hidden">
<div className="flex shrink-0 items-center justify-end gap-1">
<Tooltip>
<TooltipTrigger asChild>
<span>
@@ -235,6 +267,7 @@ export const TableField: React.FC<TableFieldProps> = ({
field={field}
updateField={updateField}
removeField={removeField}
databaseType={databaseType}
/>
</div>
</div>

View File

@@ -57,6 +57,8 @@ export const Menu: React.FC<MenuProps> = () => {
setScrollAction,
setShowCardinality,
showCardinality,
setShowFieldAttributes,
showFieldAttributes,
setShowDependenciesOnCanvas,
showDependenciesOnCanvas,
setShowMiniMapOnCanvas,
@@ -137,6 +139,10 @@ export const Menu: React.FC<MenuProps> = () => {
setShowCardinality(!showCardinality);
}, [showCardinality, setShowCardinality]);
const showOrHideFieldAttributes = useCallback(() => {
setShowFieldAttributes(!showFieldAttributes);
}, [showFieldAttributes, setShowFieldAttributes]);
const showOrHideDependencies = useCallback(() => {
setShowDependenciesOnCanvas(!showDependenciesOnCanvas);
}, [showDependenciesOnCanvas, setShowDependenciesOnCanvas]);
@@ -424,6 +430,11 @@ export const Menu: React.FC<MenuProps> = () => {
? t('menu.view.hide_cardinality')
: t('menu.view.show_cardinality')}
</MenubarItem>
<MenubarItem onClick={showOrHideFieldAttributes}>
{showFieldAttributes
? t('menu.view.hide_field_attributes')
: t('menu.view.show_field_attributes')}
</MenubarItem>
{databaseType !== DatabaseType.CLICKHOUSE &&
dependencies &&
dependencies.length > 0 ? (