diff --git a/src/hooks/user-render-sql.tsx b/src/hooks/user-render-sql.tsx index d99a32d..74514c3 100644 --- a/src/hooks/user-render-sql.tsx +++ b/src/hooks/user-render-sql.tsx @@ -36,6 +36,7 @@ export const useRenderSql = (database: DatabaseType) => { ); setCircularDependency(undefined); } catch (error) { + console.log (error) if ((error as CircularDependencyError)?.cycle) setCircularDependency((previousError) => { diff --git a/src/i18/languages/en.ts b/src/i18/languages/en.ts index 9854c5f..2406ee2 100644 --- a/src/i18/languages/en.ts +++ b/src/i18/languages/en.ts @@ -59,6 +59,18 @@ export const en = { many_to_many: "Many to Many", }, + foreign_key_actions : { + title : "Foreign key Actions" , + on_delete : "On Delete" , + on_update : "On Update" , + actions : { + no_action : "No Action" , + cascade : "Cascade" , + set_null : "Set Null" , + set_default : "Set Default" , + restrict : "Restrict" + } + } , field_settings: { title: "Field Setting", unique: "Unique", diff --git a/src/lib/field.ts b/src/lib/field.ts index 0d45df1..9810b8d 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -9,7 +9,7 @@ export enum Modifiers { CHARSET = "charset", COLLATE = "collate", VALUES = "values", - NO_DEFAULT = "no_default" , + NO_DEFAULT = "no_default", NO_UNIQUE = "no_unique" } @@ -77,8 +77,6 @@ export enum SQLiteCollation { RTrim = 'RTRIM', } - - export enum DataTypes { INTEGER = "integer", NUMERIC = "numeric", @@ -87,16 +85,25 @@ export enum DataTypes { TEXT = "text", BINARY = "binary", JSON = "JSON", - GEOMETRY = "geometry", + GEOMETRY = "geometry", ENUM = "enum" } export enum TimeDefaultValues { NO_VALUE = "no_value", - CUSTOM = "custom" , - NOW = "now" + CUSTOM = "custom", + NOW = "now" } -export const MYSQL_MAX_VAR_LENGTH = 255 ; \ No newline at end of file +export enum ForeignKeyActions { + NO_ACTION = "no_action", + CASCADE = "cascade", + SET_NULL = "set_null", + SET_DEFAULT = "set_default", + RESTRICT = "restrict", + +} + +export const MYSQL_MAX_VAR_LENGTH = 255; \ No newline at end of file diff --git a/src/lib/schemas/relationship-schema.ts b/src/lib/schemas/relationship-schema.ts index bb3e467..7f95378 100644 --- a/src/lib/schemas/relationship-schema.ts +++ b/src/lib/schemas/relationship-schema.ts @@ -35,9 +35,27 @@ export const relationships = sqliteTable('relationships', { 'many_to_one', 'many_to_many', ], - }) - .notNull() - .default('one_to_many'), + }).notNull().default('one_to_many'), + + onDelete: text('onDelete', { + enum: [ + "no_action", + "cascade", + "set_null", + "set_default", + "restrict", + ], + }).default('no_action'), + + onUpdate: text('onUpdate', { + enum: [ + "no_action", + "cascade", + "set_null", + "set_default", + "restrict", + ], + }).default('no_action'), diff --git a/src/lib/table.ts b/src/lib/table.ts index ef18692..4eb3f97 100644 --- a/src/lib/table.ts +++ b/src/lib/table.ts @@ -15,12 +15,12 @@ export interface RenderableTable extends TableType { export const toSortableTable = (table: RenderableTable): SortableTable => { - + return { tableId: table.id, - relationships: table.foreignRelationships.filter( + relationships: Array.from(new Set( table.foreignRelationships.filter( (relationship : RelationshipType) => !(relationship.targetTable.id == relationship.sourceTable.id && relationship.sourceTable.id == table.id) - ).map((relationship: RelationshipType) => relationship.sourceTable.id) + ).map((relationship: RelationshipType) => relationship.sourceTable.id))) } } diff --git a/src/pages/database/database-page.tsx b/src/pages/database/database-page.tsx index c789538..7c3fa46 100644 --- a/src/pages/database/database-page.tsx +++ b/src/pages/database/database-page.tsx @@ -42,12 +42,12 @@ import { getRelationshipSourceAndTarget } from "@/utils/relationship"; import { useModal } from "@/providers/modal-provider/modal-provider"; import { Modals } from "@/providers/modal-provider/modal-contxet"; import { Loading } from "@/components/modal/loading-modal"; -import { usePowerSync, usePowerSyncStatus } from "@powersync/react"; +import { usePowerSync } from "@powersync/react"; import { CardinalityStyle } from "@/lib/database"; -import debounce from 'lodash.debounce'; + const DatabasePage: React.FC = () => { const { open } = useModal(); const syncStatus = usePowerSync(); @@ -205,7 +205,6 @@ const DatabasePage: React.FC = () => { useHighlightedEdges(nodes, relationships, edges); const { isOverlapping, puls } = useOverlappingTables(tables); - return (
@@ -325,3 +324,4 @@ export default DatabasePage; + diff --git a/src/pages/database/db-controller/relationship-controller/relationship-accordion-item/relationship-accordion-body.tsx b/src/pages/database/db-controller/relationship-controller/relationship-accordion-item/relationship-accordion-body.tsx index 258d1a7..62e72c8 100644 --- a/src/pages/database/db-controller/relationship-controller/relationship-accordion-item/relationship-accordion-body.tsx +++ b/src/pages/database/db-controller/relationship-controller/relationship-accordion-item/relationship-accordion-body.tsx @@ -1,10 +1,11 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/tooltip/tooltip"; +import { ForeignKeyActions } from "@/lib/field"; import { Cardinality, RelationshipInsertType, RelationshipType } from "@/lib/schemas/relationship-schema"; import { useDatabaseOperations } from "@/providers/database-provider/database-provider"; import { Button, Select, SelectItem, SharedSelection } from "@heroui/react"; -import { ChevronsLeftRightEllipsis, FileMinus2, FileOutput, Trash2 } from "lucide-react"; +import { ChevronsLeftRightEllipsis, FileCog, FileKey, FileMinus2, FileOutput, Trash2 } from "lucide-react"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -14,39 +15,69 @@ interface RelationshipAccordionBodyProps { } const RelationshipAccordionBody: React.FC = ({ relationship }) => { + const [cardinality, setCardinality] = useState(new Set([relationship.cardinality])); + const [onDeleteAction, setOnDeleteAction] = useState>(new Set([])); + const [onUpdateAction, setOnUpdateAction] = useState>(new Set([])); + const { editRelationship, deleteRelationship } = useDatabaseOperations(); const { t } = useTranslation(); const changeCardinality = (keys: SharedSelection) => { - - if (keys.anchorKey && (keys.anchorKey != relationship.cardinality)) { - editRelationship({ id: relationship.id, cardinality: keys.anchorKey as Cardinality, - } as RelationshipInsertType); setCardinality(keys as any); - } + } + const changeOnDelete = (keys: SharedSelection) => { + if (keys.anchorKey && (keys.anchorKey != relationship.onDelete)) { + editRelationship({ + id: relationship.id, + onDelete: keys.anchorKey as ForeignKeyActions, + } as RelationshipInsertType); + setOnDeleteAction(keys as any); + } + } + + const changeOnUpdate = (keys: SharedSelection) => { + if (keys.anchorKey && (keys.anchorKey != relationship.onUpdate)) { + editRelationship({ + id: relationship.id, + onUpdate: keys.anchorKey as ForeignKeyActions, + } as RelationshipInsertType); + setOnUpdateAction(keys as any); + } } const removeRelationship = () => { deleteRelationship(relationship.id); - } - + } + useEffect(() => { setCardinality(new Set([relationship.cardinality])); }, [relationship.cardinality]) + useEffect(() => { + if (!relationship.onDelete) + setOnDeleteAction(new Set([ForeignKeyActions.NO_ACTION])) + else + setOnDeleteAction(new Set([relationship.onDelete as ForeignKeyActions])) + }, [relationship.onDelete]); + + useEffect(() => { + if (!relationship.onUpdate) + setOnUpdateAction(new Set([ForeignKeyActions.NO_ACTION])) + else + setOnUpdateAction(new Set([relationship.onUpdate as ForeignKeyActions])) + }, [relationship.onUpdate]); if (!relationship.sourceTable || !relationship.targetTable) return; - return (
@@ -71,7 +102,6 @@ const RelationshipAccordionBody: React.FC = ({ r @@ -111,6 +141,67 @@ const RelationshipAccordionBody: React.FC = ({ r {t("db_controller.cardinality.many_to_many")}
+ { + cardinality.values().next().value != Cardinality.many_to_many && +
+ +
+
+ + {t('db_controller.foreign_key_actions.on_delete')} + + +
+
+
+ + {t('db_controller.foreign_key_actions.on_update')} + + +
+
+
+
+ }