From 778f85d49214232a39710e47bb5d4ec41b75d427 Mon Sep 17 00:00:00 2001 From: Jonathan Fishner Date: Thu, 24 Jul 2025 15:18:33 +0300 Subject: [PATCH] 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 Co-authored-by: Guy Ben-Aharon --- src/components/select-box/select-box.tsx | 2 +- .../local-config-context.tsx | 6 + .../local-config-provider.tsx | 8 ++ src/i18n/locales/ar.ts | 4 + src/i18n/locales/bn.ts | 4 + src/i18n/locales/de.ts | 4 + src/i18n/locales/en.ts | 4 + src/i18n/locales/es.ts | 4 + src/i18n/locales/fr.ts | 4 + src/i18n/locales/gu.ts | 4 + src/i18n/locales/hi.ts | 4 + src/i18n/locales/id_ID.ts | 4 + src/i18n/locales/ja.ts | 4 + src/i18n/locales/ko_KR.ts | 4 + src/i18n/locales/mr.ts | 4 + src/i18n/locales/ne.ts | 4 + src/i18n/locales/pt_BR.ts | 4 + src/i18n/locales/ru.ts | 4 + src/i18n/locales/te.ts | 4 + src/i18n/locales/tr.ts | 4 + src/i18n/locales/uk.ts | 4 + src/i18n/locales/vi.ts | 4 + src/i18n/locales/zh_CN.ts | 4 + src/i18n/locales/zh_TW.ts | 4 + .../data/data-types/clickhouse-data-types.ts | 46 +++++-- src/lib/data/data-types/data-types.ts | 14 +- src/lib/data/data-types/generic-data-types.ts | 56 +++++++- src/lib/data/data-types/mariadb-data-types.ts | 55 +++++++- src/lib/data/data-types/mysql-data-types.ts | 27 +++- src/lib/data/data-types/oracle-data-types.ts | 30 ++++- .../data/data-types/postgres-data-types.ts | 46 ++++++- .../data/data-types/sql-server-data-types.ts | 65 ++++++++-- src/lib/data/data-types/sqlite-data-types.ts | 32 ++++- .../export-metadata/export-per-type/mssql.ts | 3 +- .../export-metadata/export-per-type/mysql.ts | 3 +- .../export-per-type/postgresql.ts | 3 +- .../data/export-metadata/export-sql-script.ts | 3 +- src/lib/domain/db-field.ts | 84 +++++++++++- .../canvas/table-node/table-node-field.tsx | 11 +- .../table-field-modal/table-field-modal.tsx | 120 +++++++++++++++++- .../table-field/table-field.tsx | 105 +++++++++------ .../editor-page/top-navbar/menu/menu.tsx | 11 ++ 42 files changed, 716 insertions(+), 98 deletions(-) diff --git a/src/components/select-box/select-box.tsx b/src/components/select-box/select-box.tsx index 839e6835..5251e507 100644 --- a/src/components/select-box/select-box.tsx +++ b/src/components/select-box/select-box.tsx @@ -227,7 +227,7 @@ export const SelectBox = React.forwardRef( onSelect={() => handleSelect( option.value, - matches?.map((match) => match.toString()) + matches?.map((match) => match?.toString()) ) } > diff --git a/src/context/local-config-context/local-config-context.tsx b/src/context/local-config-context/local-config-context.tsx index 6cf02c6f..c0f2fb0d 100644 --- a/src/context/local-config-context/local-config-context.tsx +++ b/src/context/local-config-context/local-config-context.tsx @@ -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({ showCardinality: true, setShowCardinality: emptyFn, + showFieldAttributes: true, + setShowFieldAttributes: emptyFn, + hideMultiSchemaNotification: false, setHideMultiSchemaNotification: emptyFn, diff --git a/src/context/local-config-context/local-config-provider.tsx b/src/context/local-config-context/local-config-provider.tsx index c47e4097..b582e03a 100644 --- a/src/context/local-config-context/local-config-provider.tsx +++ b/src/context/local-config-context/local-config-provider.tsx @@ -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 = ({ (localStorage.getItem(showCardinalityKey) || 'true') === 'true' ); + const [showFieldAttributes, setShowFieldAttributes] = + React.useState( + (localStorage.getItem(showFieldAttributesKey) || 'true') === 'true' + ); + const [hideMultiSchemaNotification, setHideMultiSchemaNotification] = React.useState( (localStorage.getItem(hideMultiSchemaNotificationKey) || @@ -119,6 +125,8 @@ export const LocalConfigProvider: React.FC = ({ setSchemasFilter, showCardinality, setShowCardinality, + showFieldAttributes, + setShowFieldAttributes, hideMultiSchemaNotification, setHideMultiSchemaNotification, setGithubRepoOpened, diff --git a/src/i18n/locales/ar.ts b/src/i18n/locales/ar.ts index 17d12834..217be640 100644 --- a/src/i18n/locales/ar.ts +++ b/src/i18n/locales/ar.ts @@ -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', }, diff --git a/src/i18n/locales/bn.ts b/src/i18n/locales/bn.ts index c494c985..d7e202b1 100644 --- a/src/i18n/locales/bn.ts +++ b/src/i18n/locales/bn.ts @@ -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: 'ইনডেক্স কর্ম', diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts index 93dacdd3..654b0b93 100644 --- a/src/i18n/locales/de.ts +++ b/src/i18n/locales/de.ts @@ -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', diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 945320fb..38d955ca 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -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', diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts index ebdeae2c..bf284dc0 100644 --- a/src/i18n/locales/es.ts +++ b/src/i18n/locales/es.ts @@ -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', diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index b0a0d8cf..86f84381 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -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", diff --git a/src/i18n/locales/gu.ts b/src/i18n/locales/gu.ts index 11b7c4c9..74551f2d 100644 --- a/src/i18n/locales/gu.ts +++ b/src/i18n/locales/gu.ts @@ -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: 'ઇન્ડેક્સ લક્ષણો', diff --git a/src/i18n/locales/hi.ts b/src/i18n/locales/hi.ts index 7d84c193..0ba39cf0 100644 --- a/src/i18n/locales/hi.ts +++ b/src/i18n/locales/hi.ts @@ -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: 'सूचकांक विशेषताएँ', diff --git a/src/i18n/locales/id_ID.ts b/src/i18n/locales/id_ID.ts index a2904bb5..c2601e67 100644 --- a/src/i18n/locales/id_ID.ts +++ b/src/i18n/locales/id_ID.ts @@ -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', diff --git a/src/i18n/locales/ja.ts b/src/i18n/locales/ja.ts index c39a6e16..32262abf 100644 --- a/src/i18n/locales/ja.ts +++ b/src/i18n/locales/ja.ts @@ -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: 'インデックス属性', diff --git a/src/i18n/locales/ko_KR.ts b/src/i18n/locales/ko_KR.ts index 6f9102ef..d080bae0 100644 --- a/src/i18n/locales/ko_KR.ts +++ b/src/i18n/locales/ko_KR.ts @@ -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: '인덱스 속성', diff --git a/src/i18n/locales/mr.ts b/src/i18n/locales/mr.ts index f0fc4945..9817b392 100644 --- a/src/i18n/locales/mr.ts +++ b/src/i18n/locales/mr.ts @@ -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: 'इंडेक्स गुणधर्म', diff --git a/src/i18n/locales/ne.ts b/src/i18n/locales/ne.ts index eedfb14f..733bd954 100644 --- a/src/i18n/locales/ne.ts +++ b/src/i18n/locales/ne.ts @@ -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: 'सूचक विशेषताहरू', diff --git a/src/i18n/locales/pt_BR.ts b/src/i18n/locales/pt_BR.ts index 52e05848..0d75a5dd 100644 --- a/src/i18n/locales/pt_BR.ts +++ b/src/i18n/locales/pt_BR.ts @@ -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', diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts index 36397396..0eac1c81 100644 --- a/src/i18n/locales/ru.ts +++ b/src/i18n/locales/ru.ts @@ -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: 'Атрибуты индекса', diff --git a/src/i18n/locales/te.ts b/src/i18n/locales/te.ts index 52e6f528..0c6cdda1 100644 --- a/src/i18n/locales/te.ts +++ b/src/i18n/locales/te.ts @@ -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: 'ఇండెక్స్ గుణాలు', diff --git a/src/i18n/locales/tr.ts b/src/i18n/locales/tr.ts index 53915865..346b34d6 100644 --- a/src/i18n/locales/tr.ts +++ b/src/i18n/locales/tr.ts @@ -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', diff --git a/src/i18n/locales/uk.ts b/src/i18n/locales/uk.ts index a2b3005c..ea9ef705 100644 --- a/src/i18n/locales/uk.ts +++ b/src/i18n/locales/uk.ts @@ -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: 'Атрибути індексу', diff --git a/src/i18n/locales/vi.ts b/src/i18n/locales/vi.ts index 63c26110..9f89f7f9 100644 --- a/src/i18n/locales/vi.ts +++ b/src/i18n/locales/vi.ts @@ -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', diff --git a/src/i18n/locales/zh_CN.ts b/src/i18n/locales/zh_CN.ts index 7de5dd64..ba64a6e6 100644 --- a/src/i18n/locales/zh_CN.ts +++ b/src/i18n/locales/zh_CN.ts @@ -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: '索引属性', diff --git a/src/i18n/locales/zh_TW.ts b/src/i18n/locales/zh_TW.ts index dbe5ea81..8c40a1a5 100644 --- a/src/i18n/locales/zh_TW.ts +++ b/src/i18n/locales/zh_TW.ts @@ -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: '索引屬性', diff --git a/src/lib/data/data-types/clickhouse-data-types.ts b/src/lib/data/data-types/clickhouse-data-types.ts index deffd8c5..6f4c2d68 100644 --- a/src/lib/data/data-types/clickhouse-data-types.ts +++ b/src/lib/data/data-types/clickhouse-data-types.ts @@ -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 diff --git a/src/lib/data/data-types/data-types.ts b/src/lib/data/data-types/data-types.ts index f68d6c4f..61b68b44 100644 --- a/src/lib/data/data-types/data-types.ts +++ b/src/lib/data/data-types/data-types.ts @@ -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 = z.object({ diff --git a/src/lib/data/data-types/generic-data-types.ts b/src/lib/data/data-types/generic-data-types.ts index 2c637afd..1545f9bf 100644 --- a/src/lib/data/data-types/generic-data-types.ts +++ b/src/lib/data/data-types/generic-data-types.ts @@ -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; diff --git a/src/lib/data/data-types/mariadb-data-types.ts b/src/lib/data/data-types/mariadb-data-types.ts index f00213d6..47b53207 100644 --- a/src/lib/data/data-types/mariadb-data-types.ts +++ b/src/lib/data/data-types/mariadb-data-types.ts @@ -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' }, diff --git a/src/lib/data/data-types/mysql-data-types.ts b/src/lib/data/data-types/mysql-data-types.ts index d02756cd..85b9a61a 100644 --- a/src/lib/data/data-types/mysql-data-types.ts +++ b/src/lib/data/data-types/mysql-data-types.ts @@ -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' }, diff --git a/src/lib/data/data-types/oracle-data-types.ts b/src/lib/data/data-types/oracle-data-types.ts index bb66c35e..51bdf8cd 100644 --- a/src/lib/data/data-types/oracle-data-types.ts +++ b/src/lib/data/data-types/oracle-data-types.ts @@ -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 }, diff --git a/src/lib/data/data-types/postgres-data-types.ts b/src/lib/data/data-types/postgres-data-types.ts index e202bb86..0c85058f 100644 --- a/src/lib/data/data-types/postgres-data-types.ts +++ b/src/lib/data/data-types/postgres-data-types.ts @@ -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' }, diff --git a/src/lib/data/data-types/sql-server-data-types.ts b/src/lib/data/data-types/sql-server-data-types.ts index 1dc783ef..70234f8e 100644 --- a/src/lib/data/data-types/sql-server-data-types.ts +++ b/src/lib/data/data-types/sql-server-data-types.ts @@ -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' }, diff --git a/src/lib/data/data-types/sqlite-data-types.ts b/src/lib/data/data-types/sqlite-data-types.ts index 94ff55df..12a89251 100644 --- a/src/lib/data/data-types/sqlite-data-types.ts +++ b/src/lib/data/data-types/sqlite-data-types.ts @@ -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' }, diff --git a/src/lib/data/export-metadata/export-per-type/mssql.ts b/src/lib/data/export-metadata/export-per-type/mssql.ts index 84484161..4a18496c 100644 --- a/src/lib/data/export-metadata/export-per-type/mssql.ts +++ b/src/lib/data/export-metadata/export-per-type/mssql.ts @@ -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' diff --git a/src/lib/data/export-metadata/export-per-type/mysql.ts b/src/lib/data/export-metadata/export-per-type/mysql.ts index ba31594c..ea62f9ec 100644 --- a/src/lib/data/export-metadata/export-per-type/mysql.ts +++ b/src/lib/data/export-metadata/export-per-type/mysql.ts @@ -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' diff --git a/src/lib/data/export-metadata/export-per-type/postgresql.ts b/src/lib/data/export-metadata/export-per-type/postgresql.ts index 2636ae28..45163bfc 100644 --- a/src/lib/data/export-metadata/export-per-type/postgresql.ts +++ b/src/lib/data/export-metadata/export-per-type/postgresql.ts @@ -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' diff --git a/src/lib/data/export-metadata/export-sql-script.ts b/src/lib/data/export-metadata/export-sql-script.ts index bf9cf7e1..37b0d6dd 100644 --- a/src/lib/data/export-metadata/export-sql-script.ts +++ b/src/lib/data/export-metadata/export-sql-script.ts @@ -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')) { diff --git a/src/lib/domain/db-field.ts b/src/lib/domain/db-field.ts index 093d708c..dfc1a032 100644 --- a/src/lib/domain/db-field.ts +++ b/src/lib/domain/db-field.ts @@ -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; +}; diff --git a/src/pages/editor-page/canvas/table-node/table-node-field.tsx b/src/pages/editor-page/canvas/table-node/table-node-field.tsx index 8db224d9..c9164113 100644 --- a/src/pages/editor-page/canvas/table-node/table-node-field.tsx +++ b/src/pages/editor-page/canvas/table-node/table-node-field.tsx @@ -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 = React.memo( setEditMode(true); }, []); + const { showFieldAttributes } = useLocalConfig(); + return (
= React.memo( {fieldDiffChangedType.name.split(' ')[0]} ) : ( - field.type.name.split(' ')[0] + `${field.type.name.split(' ')[0]}${showFieldAttributes ? generateDBFieldSuffix(field) : ''}` )} {field.nullable ? '?' : ''}
diff --git a/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-field/table-field-modal/table-field-modal.tsx b/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-field/table-field-modal/table-field-modal.tsx index 2144ff66..af9ca070 100644 --- a/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-field/table-field-modal/table-field-modal.tsx +++ b/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-field/table-field-modal/table-field-modal.tsx @@ -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) => void; removeField: () => void; } export const TableFieldPopover: React.FC = ({ field, + databaseType, updateField, removeField, }) => { @@ -52,6 +56,8 @@ export const TableFieldPopover: React.FC = ({ 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 = ({ prevFieldRef.current = localField; }, [localField, debouncedUpdateField, isOpen]); + const dataFieldType = useMemo( + () => findDataTypeDataById(field.type.id, databaseType), + [field.type.id, databaseType] + ); + return ( = ({ className="w-full rounded-md bg-muted text-sm" /> - {findDataTypeDataById(field.type.id) - ?.hasCharMaxLength ? ( + {dataFieldType?.fieldAttributes?.hasCharMaxLength ? (
) : null} + {dataFieldType?.fieldAttributes?.precision || + dataFieldType?.fieldAttributes?.scale ? ( +
+
+ + + setLocalField((current) => ({ + ...current, + precision: e.target.value + ? parseInt(e.target.value) + : undefined, + })) + } + className="w-full rounded-md bg-muted text-sm" + /> +
+
+ + + setLocalField((current) => ({ + ...current, + scale: e.target.value + ? parseInt(e.target.value) + : undefined, + })) + } + className="w-full rounded-md bg-muted text-sm" + /> +
+
+ ) : null}