mirror of
https://github.com/stackrender/stackrender.git
synced 2026-09-10 11:15:42 +00:00
Datatypes Seeded and Not Found page added
This commit is contained in:
+4
-1
@@ -10,6 +10,7 @@ import DiagramProvider from "./providers/diagram-provider/diagram-provider";
|
||||
|
||||
import { ToastProvider } from "@heroui/react";
|
||||
import { ModalProvider } from "./providers/modal-provider/modal-provider";
|
||||
import DatabaseHotkeysProvider from "./providers/database-hotkeys/database-hotkeys-provider";
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +27,9 @@ function App() {
|
||||
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<ModalProvider>
|
||||
{appRoutes}
|
||||
<DatabaseHotkeysProvider>
|
||||
{appRoutes}
|
||||
</DatabaseHotkeysProvider>
|
||||
</ModalProvider>
|
||||
</TooltipProvider>
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { SVGProps } from "react";
|
||||
|
||||
export function Discord(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="20" height="20" viewBox="0 0 50 50"
|
||||
className="fill-transparent"
|
||||
{...props}
|
||||
>
|
||||
|
||||
<path
|
||||
className="stroke-font/90"
|
||||
strokeWidth="3"
|
||||
fill="transparent"
|
||||
d="M 41.625 10.769531 C 37.644531 7.566406 31.347656 7.023438 31.078125 7.003906 C 30.660156 6.96875 30.261719 7.203125 30.089844 7.589844 C 30.074219 7.613281 29.9375 7.929688 29.785156 8.421875 C 32.417969 8.867188 35.652344 9.761719 38.578125 11.578125 C 39.046875 11.867188 39.191406 12.484375 38.902344 12.953125 C 38.710938 13.261719 38.386719 13.429688 38.050781 13.429688 C 37.871094 13.429688 37.6875 13.378906 37.523438 13.277344 C 32.492188 10.15625 26.210938 10 25 10 C 23.789063 10 17.503906 10.15625 12.476563 13.277344 C 12.007813 13.570313 11.390625 13.425781 11.101563 12.957031 C 10.808594 12.484375 10.953125 11.871094 11.421875 11.578125 C 14.347656 9.765625 17.582031 8.867188 20.214844 8.425781 C 20.0625 7.929688 19.925781 7.617188 19.914063 7.589844 C 19.738281 7.203125 19.34375 6.960938 18.921875 7.003906 C 18.652344 7.023438 12.355469 7.566406 8.320313 10.8125 C 6.214844 12.761719 2 24.152344 2 34 C 2 34.175781 2.046875 34.34375 2.132813 34.496094 C 5.039063 39.605469 12.972656 40.941406 14.78125 41 C 14.789063 41 14.800781 41 14.8125 41 C 15.132813 41 15.433594 40.847656 15.621094 40.589844 L 17.449219 38.074219 C 12.515625 36.800781 9.996094 34.636719 9.851563 34.507813 C 9.4375 34.144531 9.398438 33.511719 9.765625 33.097656 C 10.128906 32.683594 10.761719 32.644531 11.175781 33.007813 C 11.234375 33.0625 15.875 37 25 37 C 34.140625 37 38.78125 33.046875 38.828125 33.007813 C 39.242188 32.648438 39.871094 32.683594 40.238281 33.101563 C 40.601563 33.515625 40.5625 34.144531 40.148438 34.507813 C 40.003906 34.636719 37.484375 36.800781 32.550781 38.074219 L 34.378906 40.589844 C 34.566406 40.847656 34.867188 41 35.1875 41 C 35.199219 41 35.210938 41 35.21875 41 C 37.027344 40.941406 44.960938 39.605469 47.867188 34.496094 C 47.953125 34.34375 48 34.175781 48 34 C 48 24.152344 43.785156 12.761719 41.625 10.769531 Z M 18.5 30 C 16.566406 30 15 28.210938 15 26 C 15 23.789063 16.566406 22 18.5 22 C 20.433594 22 22 23.789063 22 26 C 22 28.210938 20.433594 30 18.5 30 Z M 31.5 30 C 29.566406 30 28 28.210938 28 26 C 28 23.789063 29.566406 22 31.5 22 C 33.433594 22 35 23.789063 35 26 C 35 28.210938 33.433594 30 31.5 30 Z"></path>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
import { SVGProps } from "react";
|
||||
|
||||
export function X(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
||||
width="100"
|
||||
height="100"
|
||||
viewBox="0 0 50 50"
|
||||
{...props}>
|
||||
<path
|
||||
|
||||
className="stroke-font/90"
|
||||
strokeWidth="3"
|
||||
fill="transparent"
|
||||
d="M 5.9199219 6 L 20.582031 27.375 L 6.2304688 44 L 9.4101562 44 L 21.986328 29.421875 L 31.986328 44 L 44 44 L 28.681641 21.669922 L 42.199219 6 L 39.029297 6 L 27.275391 19.617188 L 17.933594 6 L 5.9199219 6 z M 9.7167969 8 L 16.880859 8 L 40.203125 42 L 33.039062 42 L 9.7167969 8 z"></path>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -146,9 +146,11 @@ const Menu: React.FC = ({ }) => {
|
||||
id: "menu.help",
|
||||
title: t("menu.help"),
|
||||
children: [
|
||||
|
||||
{ id: "menu.show_docs", title: t("menu.show_docs") },
|
||||
{ id: "menu.join_discord", title: t("menu.join_discord") },
|
||||
{ id: "menu.join_discord", title: t("menu.join_discord") ,
|
||||
clickHandler : () => {
|
||||
window.open("https://discord.gg/DsN8RcPR6Y", "_blank")
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
], [t, canRedo, canUndo, undo, redo, deleteMultiTables, database, showController, openController ,changeCardinalityStyle , cardinalityStyle, resolvedTheme]);
|
||||
|
||||
@@ -48,6 +48,9 @@ const Modal: React.FC<ModalProps> = ({ isOpen, onOpenChange, className, backdrop
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<HeroUiModal
|
||||
ref={targetRef}
|
||||
|
||||
@@ -29,7 +29,6 @@ const Navbar: React.FC<Props> = ({ }) => {
|
||||
<div className=" w-full h-full flex items-center justify-center">
|
||||
{database && <RenameDatabase database={database} />}
|
||||
</div>
|
||||
|
||||
<ConnectionStatus />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,29 +10,50 @@ export interface SidebarItemProps {
|
||||
title?: string;
|
||||
icon?: React.ReactNode;
|
||||
href?: string;
|
||||
isActive? : boolean
|
||||
isActive?: boolean,
|
||||
newTab?: boolean
|
||||
}
|
||||
|
||||
|
||||
const SidebarItem: React.FC<SidebarItemProps> = ({ title, icon, href , type = "item" , isActive}) => {
|
||||
const SidebarItem: React.FC<SidebarItemProps> = ({ title, icon, href, type = "item", isActive, newTab }) => {
|
||||
|
||||
|
||||
if (type == "item")
|
||||
|
||||
|
||||
return (
|
||||
<Tooltip delayDuration={0} >
|
||||
<TooltipTrigger asChild>
|
||||
<Link to={href as string} className="p-2 rounded-md hover:bg-default-100 dark:hover:bg-background-50 sidebar-item " data-active={isActive}>
|
||||
{icon}
|
||||
</Link>
|
||||
</TooltipTrigger>
|
||||
{
|
||||
!newTab &&
|
||||
<TooltipTrigger asChild>
|
||||
|
||||
<Link
|
||||
to={href as string}
|
||||
target=""
|
||||
className="p-2 rounded-md hover:bg-default-100 dark:hover:bg-background-50 sidebar-item " data-active={isActive}>
|
||||
{icon}
|
||||
</Link>
|
||||
</TooltipTrigger>
|
||||
}
|
||||
{
|
||||
newTab &&
|
||||
<TooltipTrigger asChild>
|
||||
<a
|
||||
href={href as string}
|
||||
target="_blank"
|
||||
className="p-2 rounded-md hover:bg-default-100 dark:hover:bg-background-50 sidebar-item " data-active={isActive}>
|
||||
{icon}
|
||||
</a>
|
||||
</TooltipTrigger>
|
||||
}
|
||||
|
||||
<TooltipContent
|
||||
side="left"
|
||||
align="center"
|
||||
>
|
||||
{title}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</Tooltip >
|
||||
|
||||
)
|
||||
else {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { BookOpen, EarthLock, GitCommitHorizontal, Github, Settings, Sparkles, TableProperties, Waypoints, Workflow, Zap } from "lucide-react";
|
||||
import { Github, TableProperties, Workflow } from "lucide-react";
|
||||
import SidebarItem, { SidebarItemProps } from "./sidebar-item";
|
||||
import { useMemo } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Discord } from "../icon/discord";
|
||||
import { X } from "../icon/x";
|
||||
|
||||
interface SidebarProps {
|
||||
children?: React.ReactNode
|
||||
@@ -11,9 +13,8 @@ interface SidebarProps {
|
||||
const sidebarItemClass: string = "text-font size-4 data-[active=true]:text-primary-900";
|
||||
|
||||
const Sidebar: React.FC<SidebarProps> = ({ }) => {
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
const sidebarItems: SidebarItemProps[] = useMemo(() => [
|
||||
@@ -29,68 +30,34 @@ const Sidebar: React.FC<SidebarProps> = ({ }) => {
|
||||
href: "/database/relationships",
|
||||
isActive: location.pathname.endsWith("/database/relationships")
|
||||
},
|
||||
|
||||
{
|
||||
title: "AI",
|
||||
icon: <Sparkles className={sidebarItemClass}></Sparkles>,
|
||||
href: "/database/bot",
|
||||
isActive: location.pathname.endsWith("/database/bot")
|
||||
},
|
||||
{
|
||||
type: "divider"
|
||||
},
|
||||
{
|
||||
title: "API",
|
||||
icon: <Zap className={sidebarItemClass}></Zap>,
|
||||
href: "/database/ai-bot",
|
||||
},
|
||||
{
|
||||
title: "Authentication",
|
||||
icon: <EarthLock className={sidebarItemClass}></EarthLock>,
|
||||
href: "/database/ai-bot",
|
||||
},
|
||||
{
|
||||
title: "Controllers",
|
||||
icon: <Waypoints className={sidebarItemClass}></Waypoints>,
|
||||
href: "/database/ai-bot",
|
||||
},
|
||||
{
|
||||
type: "divider"
|
||||
},
|
||||
{
|
||||
title: "Git",
|
||||
icon: <GitCommitHorizontal className={sidebarItemClass}></GitCommitHorizontal>,
|
||||
href: "/database/ai-bot",
|
||||
},
|
||||
{
|
||||
type: "divider"
|
||||
},
|
||||
{
|
||||
title: "Project Setting",
|
||||
icon: <Settings className={sidebarItemClass}></Settings>,
|
||||
href: "/database/ai-bot",
|
||||
},
|
||||
], [location])
|
||||
|
||||
const bottomSidebarItems: SidebarItemProps[] = useMemo(() => [
|
||||
{
|
||||
title: "Github",
|
||||
icon: <Github className={sidebarItemClass}></Github>,
|
||||
href: "/database/ai-bot",
|
||||
title: "X",
|
||||
icon: <X className={sidebarItemClass}></X>,
|
||||
href: "https://github.com/KarimTamani/stackrender",
|
||||
newTab : true ,
|
||||
},
|
||||
{
|
||||
title: "Docs",
|
||||
icon: <BookOpen className={sidebarItemClass}></BookOpen>,
|
||||
href: "/database/ai-bot",
|
||||
title: "Discord",
|
||||
icon: <Discord className={sidebarItemClass}></Discord>,
|
||||
href: "https://discord.gg/DsN8RcPR6Y",
|
||||
newTab : true ,
|
||||
},
|
||||
{
|
||||
title: "Github",
|
||||
icon: <Github className={sidebarItemClass}></Github>,
|
||||
href: "https://github.com/KarimTamani/stackrender",
|
||||
newTab : true ,
|
||||
},
|
||||
], []);
|
||||
|
||||
return (
|
||||
|
||||
<aside className="h-full z-[20] flex flex-col items-between py-2 justify-between sticky top-0 duration-500 w-12 bg-sidebar pt-[56px] dark:bg-background">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
{
|
||||
sidebarItems.map((item: SidebarItemProps , index : number) => (
|
||||
sidebarItems.map((item: SidebarItemProps, index: number) => (
|
||||
<SidebarItem
|
||||
key={`top-${index}`}
|
||||
{...item}
|
||||
@@ -100,10 +67,9 @@ const Sidebar: React.FC<SidebarProps> = ({ }) => {
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-2 ">
|
||||
{
|
||||
bottomSidebarItems.map((item: SidebarItemProps, index : number) => (
|
||||
bottomSidebarItems.map((item: SidebarItemProps, index: number) => (
|
||||
<SidebarItem
|
||||
key={`bottom-${index}`}
|
||||
|
||||
{...item}
|
||||
/>
|
||||
))
|
||||
|
||||
@@ -10,6 +10,7 @@ import { format } from 'sql-formatter';
|
||||
import { DatabaseDialect, getDatabaseByDialect } from "@/lib/database";
|
||||
import { CircularDependencyError, fixCharsetPlacement, fixSQLiteColumnOrder } from "@/utils/render/render-uttils";
|
||||
import { areArraysEqual } from "@/utils/utils";
|
||||
import { decomposeManyToMany } from "@/utils/relationship";
|
||||
|
||||
const parser = new Parser();
|
||||
|
||||
@@ -17,13 +18,19 @@ const parser = new Parser();
|
||||
export const useRenderSql = (database: DatabaseType) => {
|
||||
const [sql, setSql] = useState<string>("");
|
||||
const { data_types } = useDatabaseOperations();
|
||||
const [circularDependency, setCircularDependency] = useState<CircularDependencyError | undefined>(undefined)
|
||||
const [circularDependency, setCircularDependency] = useState<CircularDependencyError | undefined>(undefined) ;
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const dbAst: any = DatabaseToAst(database, data_types);
|
||||
|
||||
const decomposedDatabase = decomposeManyToMany(database) ;
|
||||
|
||||
const dbAst: any = DatabaseToAst(decomposedDatabase, data_types);
|
||||
|
||||
let sql: string = parser.sqlify(dbAst, {
|
||||
database: getDatabaseByDialect(database.dialect).name
|
||||
});
|
||||
|
||||
if (database.dialect != DatabaseDialect.SQLITE)
|
||||
sql = fixCharsetPlacement(format(sql, { language: "sql" }));
|
||||
else
|
||||
@@ -49,7 +56,7 @@ export const useRenderSql = (database: DatabaseType) => {
|
||||
})
|
||||
|
||||
}
|
||||
}, [database]);
|
||||
}, [database , data_types]);
|
||||
|
||||
return { sql, circularDependency };
|
||||
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
import { DataInsertType } from "../schemas/data-type-schema";
|
||||
|
||||
export const MariaDbDataType: Partial<DataInsertType>[] = [
|
||||
// ===== integer =====
|
||||
{
|
||||
id: 'ce8a95f3-c0eb-4700-aefa-3147fa21377a',
|
||||
name: 'bit',
|
||||
type: 'integer',
|
||||
modifiers: ['length'] // BIT(M) where M is number of bits (1-64)
|
||||
},
|
||||
|
||||
{
|
||||
id: 'e2ff64e3-96b1-4896-8f92-1dbe5d8bd442',
|
||||
name: 'integer',
|
||||
type: 'integer',
|
||||
modifiers: ['unsigned', 'zerofill', 'length', "auto_increment"], // Same as INT
|
||||
synonyms: ['int']
|
||||
|
||||
},
|
||||
{
|
||||
id: '5b4d8b34-5f33-4660-9443-03ea2eef34fd',
|
||||
name: 'tinyint',
|
||||
type: 'integer',
|
||||
modifiers: ['unsigned', 'zerofill', 'length', "auto_increment"] // TINYINT[(M)] [UNSIGNED] [ZEROFILL]
|
||||
},
|
||||
{
|
||||
id: '6548ff67-1800-447b-8fbd-87003a43dc05',
|
||||
name: 'smallint',
|
||||
type: 'integer',
|
||||
modifiers: ['unsigned', 'zerofill', 'length', "auto_increment"] // SMALLINT[(M)] [UNSIGNED] [ZEROFILL]
|
||||
},
|
||||
{
|
||||
id: '5991fbbb-6180-4f4c-9f3f-af8f94931bae',
|
||||
name: 'mediumint',
|
||||
type: 'integer',
|
||||
modifiers: ['unsigned', 'zerofill', 'length', "auto_increment"] // MEDIUMINT[(M)] [UNSIGNED] [ZEROFILL]
|
||||
},
|
||||
{
|
||||
id: 'e7b9e8f8-6a87-4ee7-a411-af5775cd0b88',
|
||||
name: 'bigint',
|
||||
type: 'integer',
|
||||
modifiers: ['unsigned', 'zerofill', 'length', "auto_increment"] // BIGINT[(M)] [UNSIGNED] [ZEROFILL]
|
||||
},
|
||||
|
||||
// ===== numeric =====
|
||||
{
|
||||
id: '59abc150-6796-46ee-940c-88eb34eae619',
|
||||
name: 'float',
|
||||
type: 'numeric',
|
||||
modifiers: ['precision', "scale", 'unsigned', 'zerofill'] // FLOAT[(M,D)] [UNSIGNED] [ZEROFILL]
|
||||
},
|
||||
{
|
||||
id: '1ccdde6d-45ae-4ab8-9020-d0d123175037',
|
||||
name: 'double',
|
||||
type: 'numeric',
|
||||
modifiers: ['precision', "scale", 'unsigned', 'zerofill'], // DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]
|
||||
synonyms: ['double precision', 'real']
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
id: '3089e057-7ea0-4422-9f8b-b03c1c4aadb1',
|
||||
name: 'decimal',
|
||||
type: 'numeric',
|
||||
modifiers: ['precision', "scale", 'unsigned', 'zerofill'], // DECIMAL[(M[,D])] [UNSIGNED] [ZEROFILL]
|
||||
synonyms: ['dec', 'numeric', 'fixed']
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
id: '70c98043-ce75-4e17-8157-b4060b600cc0',
|
||||
name: 'boolean',
|
||||
type: 'boolean',
|
||||
synonyms: ['bool'] // BOOLEAN and BOOL are both aliases for TINYINT(1)
|
||||
|
||||
// Alias for TINYINT(1)
|
||||
},
|
||||
|
||||
// ===== date/time =====
|
||||
{
|
||||
id: 'bd2d44eb-d4dc-45bc-b1c8-39bff5c703a6',
|
||||
name: 'date',
|
||||
type: 'time',
|
||||
// No modifiers
|
||||
},
|
||||
{
|
||||
id: '2344419d-c226-42a3-bdf4-77e4181a7f81',
|
||||
name: 'datetime',
|
||||
type: 'time',
|
||||
modifiers: ['precision'] // DATETIME[(fsp)], fsp = fractional seconds precision (0-6)
|
||||
},
|
||||
{
|
||||
id: '2b2d6921-b364-4953-a94d-048c43037e35',
|
||||
name: 'timestamp',
|
||||
type: 'time',
|
||||
modifiers: ['precision'] // TIMESTAMP[(fsp)], fsp = fractional seconds precision (0-6)
|
||||
},
|
||||
{
|
||||
id: '6b40d621-d7c5-408e-b784-c839b6c36bed',
|
||||
name: 'time',
|
||||
type: 'time',
|
||||
modifiers: ['precision'] // TIME[(fsp)], fsp = fractional seconds precision (0-6)
|
||||
},
|
||||
{
|
||||
id: '2da3d963-ee00-4472-8b61-38b3f22bdeb6',
|
||||
name: 'year',
|
||||
type: 'time',
|
||||
// YEAR[(2|4)], but 2-digit YEAR is deprecated
|
||||
},
|
||||
|
||||
// ===== text =====
|
||||
{
|
||||
id: '3d1c4ddc-a71b-42c6-b935-abb571999055',
|
||||
name: 'char',
|
||||
type: 'text',
|
||||
modifiers: ['length', 'charset', 'collate'] // CHAR[(M)] [CHARACTER SET charset] [COLLATE collation]
|
||||
},
|
||||
|
||||
{
|
||||
id: 'dfba8595-d9da-4efa-b5e7-a759c07a7984',
|
||||
name: 'varchar',
|
||||
type: 'text',
|
||||
modifiers: ['length', 'charset', 'collate'], // VARCHAR(M) [CHARACTER SET charset] [COLLATE collation]
|
||||
synonyms: ['nchar', 'national char']
|
||||
},
|
||||
|
||||
{
|
||||
id: '2dc7ba21-e477-47ff-b736-836f7a6586c6',
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
modifiers: ['charset', 'collate', "no_default", "no_unique"], // TEXT [CHARACTER SET charset] [COLLATE collation]
|
||||
synonyms: ['nvarchar', 'national varchar']
|
||||
},
|
||||
{
|
||||
id: '51467e54-6257-4431-b161-477aa74857d5',
|
||||
name: 'tinytext',
|
||||
type: 'text',
|
||||
modifiers: ['charset', 'collate', "no_default", "no_unique"] // Same as TEXT
|
||||
},
|
||||
{
|
||||
id: '62399f45-0a58-4b7e-826e-d40278399b19',
|
||||
name: 'mediumtext',
|
||||
type: 'text',
|
||||
modifiers: ['charset', 'collate', "no_default", "no_unique"] // Same as TEXT
|
||||
},
|
||||
{
|
||||
id: 'd0a90fad-ed80-43c5-9388-2e416cfa07dd',
|
||||
name: 'longtext',
|
||||
type: 'text',
|
||||
modifiers: ['charset', 'collate', "no_default", "no_unique"] // Same as TEXT
|
||||
},
|
||||
|
||||
// ===== binary (bytea) =====
|
||||
{
|
||||
id: '4c08fd5b-3744-44d1-a57a-e81290a1a29b',
|
||||
name: 'blob',
|
||||
type: 'binary',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
},
|
||||
{
|
||||
id: '8183a869-7c46-4bee-9560-e5965b824aba',
|
||||
name: 'tinyblob',
|
||||
type: 'binary',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
},
|
||||
{
|
||||
id: '3edc6207-e595-45c9-af0d-056b4d991b21',
|
||||
name: 'mediumblob',
|
||||
type: 'binary',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
|
||||
},
|
||||
{
|
||||
id: 'c3cbe13f-38bf-4ef5-b448-ed9bda3c673b',
|
||||
name: 'longblob',
|
||||
type: 'binary',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
},
|
||||
{
|
||||
id: 'f2930d56-1903-4710-9c4c-d635123bfaec',
|
||||
name: 'binary',
|
||||
type: 'binary',
|
||||
modifiers: ["no_default", 'length'] // BINARY(M)
|
||||
},
|
||||
{
|
||||
id: '646d8483-d585-4b7c-8255-5bd36bf9240e',
|
||||
name: 'varbinary',
|
||||
type: 'binary',
|
||||
modifiers: ["no_default", 'length'] // VARBINARY(M)
|
||||
},
|
||||
|
||||
// ===== enum/set =====
|
||||
{
|
||||
id: '0c9fca72-ead9-4abc-8726-3074bb49c878',
|
||||
name: 'enum',
|
||||
type: 'enum',
|
||||
modifiers: ['values'] // ENUM('value1','value2',...) [CHARACTER SET charset] [COLLATE collation]
|
||||
},
|
||||
{
|
||||
id: '378b77b6-d27c-4bb1-beea-6f0423bd1776',
|
||||
name: 'set',
|
||||
type: 'enum',
|
||||
modifiers: ['values'] // SET('value1','value2',...) [CHARACTER SET charset] [COLLATE collation]
|
||||
},
|
||||
|
||||
// ===== json =====
|
||||
{
|
||||
id: '40222e5e-40a6-43a5-b5c6-2a89601ee30b',
|
||||
name: 'json',
|
||||
type: 'json',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
// No modifiers
|
||||
},
|
||||
|
||||
// ===== spatial =====
|
||||
// Spatial types generally don't have modifiers
|
||||
{
|
||||
id: '06c24cc7-0ad8-4e05-a2bd-c1b7fb881fa3',
|
||||
name: 'geometry',
|
||||
type: 'geometry',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
|
||||
},
|
||||
{
|
||||
id: '8a3363e8-ebce-4ec1-b022-b18cf52a4769',
|
||||
name: 'point',
|
||||
type: 'geometry',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
},
|
||||
{
|
||||
id: '5018b1a6-6a62-4f58-9c49-fc96efeb1b38',
|
||||
name: 'linestring',
|
||||
|
||||
type: 'geometry',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
|
||||
},
|
||||
{
|
||||
id: '0edf91e8-4f10-41b1-9fc9-3c024c5f90c4',
|
||||
name: 'polygon',
|
||||
|
||||
type: 'geometry',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
|
||||
},
|
||||
{
|
||||
id: '0be92649-f626-4dc8-a163-c86643ca6687',
|
||||
name: 'multipoint',
|
||||
|
||||
type: 'geometry',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
|
||||
},
|
||||
{
|
||||
id: 'c996968e-a83b-44af-aca0-755865cd8825',
|
||||
name: 'multilinestring',
|
||||
|
||||
type: 'geometry',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
|
||||
},
|
||||
{
|
||||
id: '3fa4450a-78b1-4243-9faf-178c607ac092',
|
||||
name: 'multipolygon',
|
||||
|
||||
type: 'geometry',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
|
||||
},
|
||||
{
|
||||
id: '584bf784-3193-4534-8078-392558fc625f',
|
||||
name: 'geometrycollection',
|
||||
|
||||
type: 'geometry',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
|
||||
},
|
||||
|
||||
// ===== MariaDB-specific additions =====
|
||||
{
|
||||
id: '7e092014-b75f-4a62-b447-0c5d4af91bc7',
|
||||
name: 'uuid',
|
||||
type: 'uuid',
|
||||
// No modifiers
|
||||
},
|
||||
{
|
||||
id: 'd6483b02-4eaf-418d-ab00-1818ca40d749',
|
||||
name: 'inet4',
|
||||
type: 'network',
|
||||
modifiers: ["no_default"]
|
||||
// No modifiers
|
||||
},
|
||||
{
|
||||
id: '90adf29b-e993-4215-8241-38dfb31a1139',
|
||||
name: 'inet6',
|
||||
type: 'network',
|
||||
modifiers: ["no_default"]
|
||||
// No modifiers
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,241 @@
|
||||
import { DataInsertType } from "../schemas/data-type-schema";
|
||||
|
||||
export const MysqlDataType : Partial<DataInsertType>[] = [
|
||||
// ===== integer =====
|
||||
|
||||
{
|
||||
id: '53a71e01-345e-4894-bc99-15148dadd1f5',
|
||||
name: 'integer',
|
||||
type: 'integer',
|
||||
modifiers: ['length', 'unsigned', 'zerofill', "auto_increment"], // Same as INT
|
||||
synonyms: ["int"]
|
||||
},
|
||||
{
|
||||
id: '838da726-648b-4b39-b589-f07c6f9ad249',
|
||||
name: 'tinyint',
|
||||
type: 'integer',
|
||||
modifiers: ['length', 'unsigned', 'zerofill', "auto_increment"] // TINYINT[(M)] [UNSIGNED] [ZEROFILL]
|
||||
},
|
||||
{
|
||||
id: '244e63ba-b172-4af5-8b82-ffbf7fb98b37',
|
||||
name: 'smallint',
|
||||
type: 'integer',
|
||||
modifiers: ['length', 'unsigned', 'zerofill', "auto_increment"] // SMALLINT[(M)] [UNSIGNED] [ZEROFILL]
|
||||
},
|
||||
{
|
||||
id: 'f34a507e-d99d-4129-967b-16436580e23a',
|
||||
name: 'mediumint',
|
||||
type: 'integer',
|
||||
modifiers: ['length', 'unsigned', 'zerofill', "auto_increment"] // MEDIUMINT[(M)] [UNSIGNED] [ZEROFILL]
|
||||
},
|
||||
{
|
||||
id: '37f5a67a-5366-4b0a-aa2d-b94f7cae0c09',
|
||||
name: 'bigint',
|
||||
type: 'integer',
|
||||
modifiers: ['length', 'unsigned', 'zerofill', "auto_increment"] // BIGINT[(M)] [UNSIGNED] [ZEROFILL]
|
||||
},
|
||||
|
||||
// ===== numeric =====
|
||||
{
|
||||
id: '40183231-7a46-4f40-9b54-69a573ce61d1',
|
||||
name: 'float',
|
||||
type: 'numeric',
|
||||
modifiers: ['precision', "scale", 'unsigned', 'zerofill'],
|
||||
synonyms: ['float4']
|
||||
},
|
||||
{
|
||||
id: 'b1b1ccf9-b349-453e-a3dd-1ca7751d27f1',
|
||||
name: 'double',
|
||||
type: 'numeric',
|
||||
modifiers: ['precision', "scale", 'unsigned', 'zerofill'],
|
||||
synonyms: ['double precision', 'real', 'float8']
|
||||
|
||||
},
|
||||
{
|
||||
id: '094e7c4c-ae7f-414f-b7aa-6d44e7f357c2',
|
||||
name: 'decimal',
|
||||
type: 'numeric',
|
||||
modifiers: ['precision', "scale", 'unsigned', 'zerofill'],
|
||||
synonyms: ['dec', 'numeric', 'fixed']
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
id: '73eac02b-27e6-4d74-a161-00809c85be01',
|
||||
name: 'boolean',
|
||||
type: 'boolean',
|
||||
synonyms: ['bool'],
|
||||
},
|
||||
|
||||
// ===== date =====
|
||||
{
|
||||
id: '8fb4b59c-ab26-4377-a329-aca5bce94ef7',
|
||||
name: 'date',
|
||||
type: 'time',
|
||||
// No modifiers
|
||||
},
|
||||
|
||||
// ===== timestamp =====
|
||||
{
|
||||
id: 'c53ab7b7-a453-4669-80c8-cad828e4c95d',
|
||||
name: 'datetime',
|
||||
type: 'time',
|
||||
modifiers: ['precision'] // DATETIME[(fsp)], fsp = fractional seconds (0-6)
|
||||
},
|
||||
{
|
||||
id: 'c2c12ebd-9e71-45d9-979b-868335fbf29a',
|
||||
name: 'timestamp',
|
||||
type: 'time',
|
||||
modifiers: ['precision'] // TIMESTAMP[(fsp)], fsp = fractional seconds (0-6)
|
||||
},
|
||||
|
||||
// ===== time =====
|
||||
{
|
||||
id: '0097a5e9-2e8b-46f8-a27b-229e05c0ad3e',
|
||||
name: 'time',
|
||||
type: 'time',
|
||||
modifiers: ['precision'] // TIME[(fsp)], fsp = fractional seconds (0-6)
|
||||
},
|
||||
{
|
||||
id: 'd22941e8-c036-4fda-b7b7-003e5e9d92d5',
|
||||
name: 'year',
|
||||
type: 'time',
|
||||
// YEAR[(2|4)], but 2-digit YEAR is deprecated
|
||||
},
|
||||
|
||||
{
|
||||
id: "98bf3653-ef15-4528-8319-9053f7ce0b9a",
|
||||
name: "char",
|
||||
type: "text",
|
||||
modifiers: ["length", "charset", "collate"],
|
||||
synonyms: ["nchar", "national char"] // CHAR = NCHAR = NATIONAL CHAR
|
||||
},
|
||||
{
|
||||
id: "e9b4026d-67bb-4478-812a-760a1312dcb3",
|
||||
name: "varchar",
|
||||
type: "text",
|
||||
modifiers: ["length", "charset", "collate"] ,
|
||||
synonyms: ["nvarchar", "national varchar"] // VARCHAR = NVARCHAR = NATIONAL VARCHAR
|
||||
|
||||
},
|
||||
{
|
||||
id: "1caa0a4a-fa8e-4ca1-85af-ed3355f91789",
|
||||
name: "text",
|
||||
type: "text",
|
||||
modifiers: ["charset", "collate", "no_default", "no_unique"]
|
||||
},
|
||||
{
|
||||
id: "04cb1391-b46b-452f-8e36-cd90fbbd0c9b",
|
||||
name: "tinytext",
|
||||
type: "text",
|
||||
modifiers: ["charset", "collate", "no_default", "no_unique"]
|
||||
},
|
||||
{
|
||||
id: "0eb12f0c-d9b9-4355-b097-cffb5174ec48",
|
||||
name: "mediumtext",
|
||||
type: "text",
|
||||
modifiers: ["charset", "collate", "no_default", "no_unique"]
|
||||
},
|
||||
{
|
||||
id: "3f7402aa-d1e4-416a-9f0c-39063a5253ef",
|
||||
name: "longtext",
|
||||
type: "text",
|
||||
modifiers: ["charset", "collate", "no_default", "no_unique"]
|
||||
},
|
||||
|
||||
// ===== binary =====
|
||||
{
|
||||
id: '9437b53d-3ad0-4079-9b51-afd433e91085',
|
||||
name: 'binary',
|
||||
type: 'binary',
|
||||
modifiers: ["no_default", 'length'] // BINARY(M)
|
||||
},
|
||||
{
|
||||
id: 'ccbc6f5f-07d4-43e2-b3e2-ca802e373a7b',
|
||||
name: 'varbinary',
|
||||
type: 'binary',
|
||||
modifiers: ["no_default", 'length'] // VARBINARY(M)
|
||||
},
|
||||
{
|
||||
id: '5f03ac95-b931-444f-9837-519a785cc098',
|
||||
name: 'blob',
|
||||
type: 'binary',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
// BLOB
|
||||
},
|
||||
{
|
||||
id: '9d677b18-2a4a-435f-ac8a-4ba0ceef9148',
|
||||
name: 'tinyblob',
|
||||
type: 'binary',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
// TINYBLOB
|
||||
},
|
||||
{
|
||||
id: 'ab7f73d6-bdaf-4485-a4da-d94467f157d7',
|
||||
name: 'mediumblob',
|
||||
type: 'binary',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
// MEDIUMBLOB
|
||||
},
|
||||
{
|
||||
id: '9367631e-3e05-4b85-9937-9b7e80ac7430',
|
||||
name: 'longblob',
|
||||
type: 'binary',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
// LONGBLOB
|
||||
},
|
||||
|
||||
// ===== enum =====
|
||||
{
|
||||
id: '0e964e0c-f1f0-4bfd-84f7-dd82f07973aa',
|
||||
name: 'enum',
|
||||
type: 'enum',
|
||||
modifiers: ['values'] // ENUM('value1','value2',...) [CHARACTER SET charset] [COLLATE collation]
|
||||
},
|
||||
{
|
||||
id: '793c840c-b11e-484d-aaed-c9bbdee4b422',
|
||||
name: 'set',
|
||||
type: 'enum',
|
||||
modifiers: ['values'] // SET('value1','value2',...) [CHARACTER SET charset] [COLLATE collation]
|
||||
},
|
||||
|
||||
// ===== json =====
|
||||
{
|
||||
id: '9c9efa25-4db9-44f8-905b-948608f9119f',
|
||||
name: 'json',
|
||||
type: 'json',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
// No modifiers
|
||||
},
|
||||
|
||||
{
|
||||
id: '821e41e2-c7d0-4a89-86cf-501df764eb15',
|
||||
name: 'geometry',
|
||||
type: 'geometry',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
},
|
||||
{
|
||||
id: '5de94b34-3a89-4650-98f6-8779bf0370db',
|
||||
name: 'point',
|
||||
type: 'geometry',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
},
|
||||
{
|
||||
id: '353225e7-0da6-4bdc-8fd9-101bf3c5b8f3',
|
||||
name: 'linestring',
|
||||
type: 'geometry',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
},
|
||||
{
|
||||
id: '71f34268-df34-448a-bf4f-95a67e03653c',
|
||||
name: 'polygon',
|
||||
type: 'geometry',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
},
|
||||
{
|
||||
id: '3ed65825-6bd3-4d3a-82aa-be8ff168d450',
|
||||
name: 'geometrycollection',
|
||||
type: 'geometry',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,62 @@
|
||||
import { DataInsertType } from "../schemas/data-type-schema";
|
||||
|
||||
export const PostgresDataType: Partial<DataInsertType>[] = [
|
||||
{
|
||||
id: 'ee0289ee-8cc8-43c0-b67d-321cbc4585d1', name: 'smallint', type: 'integer', modifiers: ["auto_increment"],
|
||||
synonyms: ['int2']
|
||||
},
|
||||
{
|
||||
id: '96ca7315-2daf-489e-a5c6-1f6757dea9d9', name: 'integer', type: 'integer', modifiers: ["auto_increment"],
|
||||
synonyms: ['int', 'int4']
|
||||
},
|
||||
{
|
||||
id: 'e256e7b2-355a-45f4-aee7-6b39efa853a0', name: 'bigint', type: 'integer', modifiers: ["auto_increment"],
|
||||
synonyms: ['int8']
|
||||
},
|
||||
|
||||
{ id: 'b11e83d0-57c4-45fd-8f7d-c09a94758af0', name: 'numeric', type: 'numeric', modifiers: ['precision', 'scale'], synonyms: ['decimal'] },
|
||||
{ id: '8c3e6086-6372-4ff1-a60c-7ca75f31d8e7', name: 'real', type: 'numeric', synonyms: ['float4', 'float'] },
|
||||
{ id: '3774463e-1076-4dcb-95e7-f695fa769899', name: 'double precision', type: 'numeric', synonyms: ['float8'] },
|
||||
{ id: 'd85b45d9-ea58-4d80-8589-b2489c64b6a9', name: 'money', type: 'numeric' },
|
||||
|
||||
|
||||
{ id: '0173eda2-1e61-45bd-99cc-6a1a8df93efa', name: 'varchar', type: 'text', modifiers: ['length'], synonyms: ['character varying'] },
|
||||
{ id: '87c39193-9592-488c-9864-8643cef6ce12', name: 'char', type: 'text', modifiers: ['length'], synonyms: ['character'] },
|
||||
{ id: 'ec45a87a-00dc-422a-ac05-578f9bf74fdb', name: 'text', type: 'text' },
|
||||
|
||||
{ id: 'e75dbf56-41c4-4570-b141-e693b6fe1612', name: 'bytea', type: 'binary', modifiers: ["no_default"] },
|
||||
{ id: '1d7f3282-e377-4252-bd49-95277c9c637c', name: 'bit', type: 'binary', modifiers: ['length', 'no_default'] },
|
||||
{ id: 'bd35c546-89d2-407a-accc-101851f2b62d', name: 'varbit', type: 'binary', modifiers: ['length', "no_default"], synonyms: ['bit varying'] },
|
||||
|
||||
{
|
||||
id: '11f5ce77-a0c8-44b0-9fbb-4a4b65140997', name: 'timestamp', type: 'time', modifiers: ['precision'], synonyms: [
|
||||
'timestamp without time zone',
|
||||
'timestamptz', // often mistaken but technically not equivalent
|
||||
]
|
||||
},
|
||||
{ id: 'bd95631d-c1c4-4cc2-a715-b613369ab097', name: 'date', type: 'time', },
|
||||
{ id: 'ea3b2c12-931e-4c88-bda5-07efa36ae178', name: 'time', type: 'time', modifiers: ['precision'], synonyms: ['time without time zone'] },
|
||||
|
||||
|
||||
{ id: '3763db96-0ad8-45ef-8510-83000e398263', name: 'boolean', type: 'boolean', synonyms: ['bool'] },
|
||||
|
||||
{ id: '5baecfc7-f76d-4184-bb10-586dfb95ac37', name: 'enum', type: 'enum', modifiers: ['values'] },
|
||||
|
||||
{ id: "9660224d-cc98-4204-b4db-cbce58f1257d", name: 'point', type: 'geometric', modifiers: ["no_default"] },
|
||||
{ id: 'e390e7a6-6fcb-4417-91ec-f663db80e47f', name: 'line', type: 'geometric', modifiers: ["no_default"] },
|
||||
{ id: 'd6cc5854-f88c-4b92-98ce-7b4016022fe1', name: 'lseg', type: 'geometric', modifiers: ["no_default"] },
|
||||
{ id: '4ce5a962-0194-4298-91f6-4647b0a12b13', name: 'box', type: 'geometric', modifiers: ["no_default"] },
|
||||
{ id: 'b95eec3d-c6b1-40f0-8fe7-19b4a6212cfd', name: 'path', type: 'geometric', modifiers: ["no_default"] },
|
||||
{ id: 'b8de3385-798b-4b3b-983f-f3cc61c3c3ac', name: 'polygon', type: 'geometric', modifiers: ["no_default"] },
|
||||
{ id: '7b3db3bc-2334-4fe5-a5d7-1ad61f4cfb4f', name: 'circle', type: 'geometric', modifiers: ["no_default"] },
|
||||
|
||||
{ id: 'f5ad8fc7-4dfb-4e58-b5f5-348a08354056', name: 'uuid', type: 'uuid', },
|
||||
{ id: 'a5dfb0db-377b-4ff7-b4c1-3c8e705d71aa', name: 'json', type: 'json', modifiers: ["no_default"] },
|
||||
{ id: '1a8e41ae-fc5e-4bd9-9919-960e07d7b3d4', name: 'jsonb', type: 'json', modifiers: ["no_default"] },
|
||||
{ id: 'e55e9dc4-4dd3-47f5-a9db-10c3b6f1295b', name: 'xml', type: 'xml', modifiers: ["no_default"] },
|
||||
{ id: '2a52974a-2b00-4cd8-8264-fd41e2f77683', name: 'cidr', type: 'network', modifiers: ["no_default"] },
|
||||
{ id: 'ac857b8e-8803-4635-ae2c-1be1c1aa8324', name: 'inet', type: 'network', modifiers: ["no_default"] },
|
||||
{ id: '83f9691a-1b12-4663-a24a-f019bb5f04c2', name: 'macaddr', type: 'network', modifiers: ["no_default"] },
|
||||
{ id: 'fdc52c94-6bd4-47d8-a5e7-6a43e5e517e9', name: 'tsvector', type: 'textsearch', modifiers: ["no_default"] },
|
||||
{ id: '1b06287e-7e9f-4a3f-8c61-5f0a18f7f91c', name: 'tsquery', type: 'textsearch', modifiers: ["no_default"] },
|
||||
];
|
||||
@@ -0,0 +1,34 @@
|
||||
import { PowerSyncSQLiteDatabase } from "@powersync/drizzle-driver";
|
||||
import { data_types, DataInsertType } from "../schemas/data-type-schema";
|
||||
import { MysqlDataType } from "./mysql_data_types";
|
||||
import { PostgresDataType } from "./postgres_data_types";
|
||||
import { SqliteDataTypes } from "./sqlite_data_types";
|
||||
import { MariaDbDataType } from "./mariadb_data_types";
|
||||
import { DatabaseDialect } from "../database";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const seedDataTypes = async (db: any) => {
|
||||
const existing = await db.query.data_types.findFirst();
|
||||
if (!existing) {
|
||||
// seeding data types .
|
||||
return await db.insert(data_types).values([
|
||||
...mapToDataType(MysqlDataType , DatabaseDialect.MYSQL) ,
|
||||
...mapToDataType(PostgresDataType , DatabaseDialect.POSTGRES) ,
|
||||
...mapToDataType(SqliteDataTypes , DatabaseDialect.SQLITE) ,
|
||||
...mapToDataType(MariaDbDataType , DatabaseDialect.MARIADB)
|
||||
] as DataInsertType[])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapToDataType = (dataTypes: Partial<DataInsertType>[], dialect: DatabaseDialect): DataInsertType[] => {
|
||||
return dataTypes.map((dataType: Partial<DataInsertType>) => ({
|
||||
...dataType,
|
||||
dialect,
|
||||
modifiers: dataType.modifiers ? JSON.stringify(dataType.modifiers) : undefined,
|
||||
synonyms: dataType.synonyms ? JSON.stringify(dataType.synonyms) : undefined
|
||||
} as DataInsertType))
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import { DataInsertType } from "../schemas/data-type-schema";
|
||||
|
||||
export const SqliteDataTypes: Partial<DataInsertType>[] = [
|
||||
// ===== INTEGER =====
|
||||
{
|
||||
id: 'dea77641-e050-4392-9c14-e53cafa1bcab', // using canonical 'integer'
|
||||
name: 'integer',
|
||||
type: 'integer',
|
||||
modifiers: ['auto_increment'], // Only valid with PRIMARY KEY
|
||||
synonyms: [
|
||||
'int',
|
||||
'tinyint',
|
||||
'smallint',
|
||||
'mediumint',
|
||||
'bigint',
|
||||
'unsigned big int',
|
||||
'int2',
|
||||
'int8'
|
||||
]
|
||||
},
|
||||
|
||||
// ===== TEXT =====
|
||||
{
|
||||
id: '862ee99c-a1d9-4652-a35a-a545712d39db', // 'text'
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
modifiers: ['length'], // Length ignored but can exist in UI
|
||||
synonyms: [
|
||||
'character',
|
||||
'varchar',
|
||||
'varying character',
|
||||
'nchar',
|
||||
'native character',
|
||||
'nvarchar',
|
||||
'clob'
|
||||
]
|
||||
},
|
||||
|
||||
// ===== REAL =====
|
||||
{
|
||||
id: '79f8e7a8-b36b-488a-9703-aa7593dcdfa4', // 'real'
|
||||
name: 'real',
|
||||
type: 'numeric',
|
||||
synonyms: [
|
||||
'float',
|
||||
'double',
|
||||
'double precision'
|
||||
]
|
||||
},
|
||||
|
||||
// ===== NUMERIC =====
|
||||
{
|
||||
id: '36ea1bc0-8f4f-4d9d-9a74-31458a65062c', // 'numeric'
|
||||
name: 'numeric',
|
||||
type: 'numeric',
|
||||
modifiers: ['precision'], // Precision/scale ignored
|
||||
synonyms: [
|
||||
'decimal'
|
||||
]
|
||||
},
|
||||
|
||||
// ===== BLOB =====
|
||||
{
|
||||
id: '577161ce-7085-4484-af44-d25304c8c63b', // 'blob'
|
||||
name: 'blob',
|
||||
type: 'binary',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
},
|
||||
|
||||
// ===== BOOLEAN =====
|
||||
{
|
||||
id: '263a800f-e6c1-4287-91ad-d6688cb9f1e1', // 'boolean'
|
||||
name: 'boolean',
|
||||
type: 'boolean'
|
||||
},
|
||||
|
||||
// ===== DATE / TIME =====
|
||||
{
|
||||
id: 'c438e00b-f7f8-4343-807d-96a6a3fff1c4', // 'date'
|
||||
name: 'date',
|
||||
type: 'time'
|
||||
},
|
||||
{
|
||||
id: 'ef9455e5-3d56-48bd-8ba6-4c4d13a72fb9', // 'time'
|
||||
name: 'time',
|
||||
type: 'time'
|
||||
},
|
||||
{
|
||||
id: '0e2a61ea-05dc-4b67-ab84-04caac829d57', // 'datetime'
|
||||
name: 'datetime',
|
||||
type: 'time'
|
||||
},
|
||||
|
||||
// ===== JSON =====
|
||||
{
|
||||
id: 'a8cdd25f-dbd5-4582-b42d-641e5862de01', // 'json'
|
||||
name: 'json',
|
||||
type: 'json',
|
||||
modifiers: ["no_default", "no_unique"]
|
||||
}
|
||||
];
|
||||
@@ -1,6 +1,7 @@
|
||||
import { InferSelectModel, relations } from 'drizzle-orm';
|
||||
import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
||||
import { fields } from './field-schema';
|
||||
import { DatabaseDialect } from '../database';
|
||||
|
||||
export const data_types = sqliteTable('data_types', {
|
||||
|
||||
@@ -28,4 +29,12 @@ export interface DataType extends InferSelectModel<typeof data_types> {
|
||||
modifiers: string | null | any;
|
||||
synonyms: string | null | any;
|
||||
|
||||
};
|
||||
|
||||
|
||||
export interface DataInsertType extends InferSelectModel<typeof data_types> {
|
||||
|
||||
modifiers: string[] | null | any;
|
||||
synonyms: string[] | null | any;
|
||||
dialect: DatabaseDialect
|
||||
};
|
||||
@@ -4,11 +4,9 @@ import { Button, cn, Divider, Navbar } from "@heroui/react";
|
||||
import { useOnViewportChange, useReactFlow } from "@xyflow/react";
|
||||
|
||||
import { LayoutGrid, Redo, Scan, Undo, ZoomIn, ZoomOut } from "lucide-react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
|
||||
|
||||
interface DbControlButtons {
|
||||
adjustPositions: () => void,
|
||||
}
|
||||
@@ -16,8 +14,8 @@ interface DbControlButtons {
|
||||
const ZOOM_DURATION = 100
|
||||
|
||||
const DatabaseControlButtons: React.FC<DbControlButtons> = ({ adjustPositions }) => {
|
||||
const { zoomIn, zoomOut, fitView , getZoom} = useReactFlow();
|
||||
console.log ()
|
||||
const { zoomIn, zoomOut, fitView, getZoom } = useReactFlow();
|
||||
|
||||
const [zoom, setZoom] = useState<string>(
|
||||
`${Math.round(getZoom() * 100)}%`
|
||||
);
|
||||
@@ -53,7 +51,12 @@ const DatabaseControlButtons: React.FC<DbControlButtons> = ({ adjustPositions })
|
||||
maxZoom: 1,
|
||||
})
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Navbar className="flex rounded-md border-1 border-default-200 bg-background dark:bg-transparent bg-default dark:border-divider " isBlurred
|
||||
classNames={{
|
||||
|
||||
@@ -42,20 +42,19 @@ 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 } from "@powersync/react";
|
||||
import { usePowerSync } from "@powersync/react";
|
||||
import { CardinalityStyle } from "@/lib/database";
|
||||
|
||||
|
||||
|
||||
|
||||
const DatabasePage: React.FC = () => {
|
||||
const { open } = useModal();
|
||||
const syncStatus = usePowerSync();
|
||||
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { resolvedTheme } = useTheme();
|
||||
// Extract database state and operations
|
||||
const { database, getField, databases, isSwitchingDatabase, isLoading, isFetching, isSyncing } = useDatabase();
|
||||
const { database, getField, databases, isSwitchingDatabase, isLoading, currentDatabaseId } = useDatabase();
|
||||
|
||||
const { updateTablePositions, deleteMultiTables, deleteMultiRelationships, createRelationship } = useDatabaseOperations();
|
||||
|
||||
@@ -77,38 +76,36 @@ const DatabasePage: React.FC = () => {
|
||||
const edgeTypes = useMemo(() => ({ 'relationship-edge': Relationship }), []);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (isSyncing)
|
||||
if (isLoading)
|
||||
return;
|
||||
|
||||
// no database selected , open (open database) Modal
|
||||
if (!database && databases?.length > 0) {
|
||||
|
||||
open(Modals.OPEN_DATABASE, {
|
||||
closable: false
|
||||
})
|
||||
}
|
||||
else if (databases.length == 0) {
|
||||
else if (databases.length == 0) {
|
||||
// there is no databases in the first place to select from ,
|
||||
// we have to create a new one .
|
||||
open(Modals.CREATE_DATABASE, {
|
||||
closable: false
|
||||
});
|
||||
}
|
||||
}, [database?.id, isSyncing])
|
||||
|
||||
useEffect(() => {
|
||||
if (isSwitchingDatabase) {
|
||||
setNodes([]);
|
||||
setEdges([]);
|
||||
} else {
|
||||
fitView({
|
||||
duration: 500
|
||||
});
|
||||
}
|
||||
}, [isSwitchingDatabase])
|
||||
}, [database?.id, isLoading])
|
||||
|
||||
useEffect(() => {
|
||||
setNodes([]);
|
||||
setEdges([]);
|
||||
}, [currentDatabaseId])
|
||||
|
||||
// Called when a connection is made between fields
|
||||
const onConnect = useCallback(async (connection: Connection) => {
|
||||
|
||||
const sourceId: string | undefined = (connection.sourceHandle as string).split("_").pop();
|
||||
const targetId: string = (connection.targetHandle as string).replace(TARGET_PREFIX, "");
|
||||
|
||||
@@ -125,7 +122,6 @@ const DatabasePage: React.FC = () => {
|
||||
targetTableId,
|
||||
sourceFieldId,
|
||||
targetFieldId,
|
||||
|
||||
} as RelationshipInsertType);
|
||||
|
||||
} else {
|
||||
@@ -150,9 +146,7 @@ const DatabasePage: React.FC = () => {
|
||||
|
||||
const nodeRemoveChanges: NodeRemoveChange[] = changes.filter((change: NodeChange) => change.type == "remove");
|
||||
// Save new positions to the database
|
||||
|
||||
if (nodePositionChanges.length > 0)
|
||||
|
||||
updateTablePositions(nodePositionChanges.map((change: NodePositionChange) => ({
|
||||
id: change.id,
|
||||
posX: change.position?.x,
|
||||
@@ -205,11 +199,12 @@ const DatabasePage: React.FC = () => {
|
||||
useHighlightedEdges(nodes, relationships, edges);
|
||||
const { isOverlapping, puls } = useOverlappingTables(tables);
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<div className="w-full h-screen flex relative overflow-hidden">
|
||||
{
|
||||
(isSwitchingDatabase || isLoading || !(syncStatus.currentStatus as any).options.hasSynced) && <Loading />
|
||||
(isSwitchingDatabase || isLoading) && <Loading />
|
||||
}
|
||||
<div className="flex max-w-full">
|
||||
<DBController />
|
||||
|
||||
@@ -23,22 +23,7 @@ interface SqlPreviewProps {
|
||||
}
|
||||
|
||||
|
||||
|
||||
const sqlExample : string = `
|
||||
CREATE TABLE projects (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE tasks (
|
||||
id INTEGER PRIMARY KEY,
|
||||
title VARCHAR(100) NOT NULL,
|
||||
project_id INT,
|
||||
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
`
|
||||
|
||||
|
||||
const SqlPreview: React.FC<SqlPreviewProps> = ({ tableFilterIds }) => {
|
||||
|
||||
@@ -60,13 +45,6 @@ const SqlPreview: React.FC<SqlPreviewProps> = ({ tableFilterIds }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const parser = new Parser() ;
|
||||
const ast = parser.astify(sqlExample , {
|
||||
database : "MySql"
|
||||
}) ;
|
||||
|
||||
console.log (ast)
|
||||
if (circularDependency)
|
||||
addToast({
|
||||
title: t("db_controller.circular_dependency.title"),
|
||||
|
||||
+1
-1
@@ -83,6 +83,7 @@ const fieldDefautlValue: React.FC<FieldDefaultValueProps> = ({ field }) => {
|
||||
multiSelect: field.type?.type == DataTypes.ENUM && field.type?.name == "set"
|
||||
}
|
||||
}, [field]);
|
||||
|
||||
const [selectedValues, setSelectedValues] = useState<string[]>(() => {
|
||||
if (!field.defaultValue)
|
||||
return []
|
||||
@@ -98,7 +99,6 @@ const fieldDefautlValue: React.FC<FieldDefaultValueProps> = ({ field }) => {
|
||||
if (field.values && field.values.length > 0) {
|
||||
try {
|
||||
const jsonValues: string[] = JSON.parse(field.values);
|
||||
|
||||
setValues(jsonValues);
|
||||
} catch (error) {
|
||||
setValues([]);
|
||||
|
||||
+2
-2
@@ -51,11 +51,11 @@ const FieldItem: React.FC<Props> = ({ field }) => {
|
||||
} as FieldType);
|
||||
}
|
||||
const updateFieldType = (key: Key | null) => {
|
||||
|
||||
|
||||
const dataType: DataType | undefined = data_types.find((dataTypes: DataType) => dataTypes.id == key);
|
||||
if (!dataType)
|
||||
return;
|
||||
|
||||
|
||||
if (key != null) {
|
||||
editField({
|
||||
id: field.id,
|
||||
|
||||
+1
-2
@@ -35,9 +35,8 @@ const IndexesList: React.FC<IndexesListProps> = ({ indices, fields, tableId }) =
|
||||
return (
|
||||
<div className="w-full flex-col space-y-2">
|
||||
{
|
||||
indices.map((index: IndexType) => <IndexItem index={index} fields={fields} />)
|
||||
indices.map((index: IndexType) => <IndexItem index={index} fields={fields} key={index.id}/>)
|
||||
}
|
||||
|
||||
<Button
|
||||
variant="flat"
|
||||
radius="sm"
|
||||
|
||||
@@ -32,7 +32,6 @@ const TablesController: React.FC = ({ }) => {
|
||||
const nameRef: Ref<HTMLInputElement> = useRef<HTMLInputElement>(null);
|
||||
const accordionRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
||||
useEffect(() => searchTables(), [allTables]);
|
||||
|
||||
const addNewTable = useCallback(async () => {
|
||||
@@ -53,7 +52,6 @@ const TablesController: React.FC = ({ }) => {
|
||||
id: v4(),
|
||||
name: "id",
|
||||
isPrimary: true,
|
||||
unique: true,
|
||||
typeId: getInteger()?.id,
|
||||
autoIncrement: true,
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@ import { v4 } from "uuid";
|
||||
|
||||
export const CreateDatabaseModal: React.FC<ModalProps> = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const { isOpen, onOpenChange } = props ;
|
||||
|
||||
|
||||
const { isOpen, onOpenChange } = props;
|
||||
|
||||
|
||||
const [isValid, setIsValid] = useState<boolean>(false);
|
||||
const [selectedDbType, setSelectedDbType] = useState<string[]>([DBTypes[0].dialect]);
|
||||
const [dbName, setDbName] = useState<string>("db_example");
|
||||
@@ -54,6 +54,7 @@ export const CreateDatabaseModal: React.FC<ModalProps> = (props) => {
|
||||
|
||||
return (
|
||||
<Modal
|
||||
{...props}
|
||||
isOpen={isOpen}
|
||||
onOpenChange={onOpenChange}
|
||||
title={t("modals.pick_database")}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
const NotFoundPage : React.FC = ({}) => {
|
||||
return(
|
||||
<section >
|
||||
<div className="mx-auto max-w-screen-xl px-4 py-8 lg:px-6 lg:py-16">
|
||||
<div className="mx-auto max-w-screen-sm text-center">
|
||||
<h1 className="mb-4 text-7xl font-extrabold text-primary lg:text-9xl">
|
||||
404
|
||||
</h1>
|
||||
<p className="mb-4 text-3xl font-bold md:text-4xl text-font/90">
|
||||
Something's missing.
|
||||
</p>
|
||||
<p className="mb-4 text-lg font-light text-font/70">
|
||||
Sorry, we can't find that page. You'll find lots to
|
||||
explore on the home page.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default NotFoundPage ;
|
||||
@@ -12,7 +12,7 @@ interface Props { children: React.ReactNode };
|
||||
const DatabaseHistoryProvider: React.FC<Props> = ({ children }) => {
|
||||
|
||||
const udpateDbFlag = useRef(false);
|
||||
const { database, isLoading, isFetching } = useDatabase();
|
||||
const { database, isLoading } = useDatabase();
|
||||
const { executeDbDiffOps } = useDatabaseOperations();
|
||||
const [isProcessing, setIsProcessing] = useState<boolean>(false);
|
||||
|
||||
@@ -24,7 +24,7 @@ const DatabaseHistoryProvider: React.FC<Props> = ({ children }) => {
|
||||
}, [database?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (database && datatbaseState.present && !isLoading && !isFetching) {
|
||||
if (database && datatbaseState.present && !isLoading ) {
|
||||
udpateDbFlag.current = false;
|
||||
|
||||
const presentHash: string | undefined = hash(datatbaseState.present, { algorithm: 'sha1' });
|
||||
@@ -39,18 +39,18 @@ const DatabaseHistoryProvider: React.FC<Props> = ({ children }) => {
|
||||
|
||||
|
||||
const undo = useCallback(() => {
|
||||
if (!isProcessing && !isLoading && !isFetching) {
|
||||
if (!isProcessing && !isLoading ) {
|
||||
udpateDbFlag.current = true;
|
||||
undoChanges();
|
||||
}
|
||||
}, [undoChanges, udpateDbFlag, isProcessing , isLoading , isFetching]);
|
||||
}, [undoChanges, udpateDbFlag, isProcessing , isLoading ]);
|
||||
|
||||
const redo = useCallback(() => {
|
||||
if (!isProcessing && !isLoading && !isFetching) {
|
||||
if (!isProcessing && !isLoading ) {
|
||||
udpateDbFlag.current = true;
|
||||
redoChanges();
|
||||
}
|
||||
}, [redoChanges, udpateDbFlag, isProcessing, isLoading , isFetching]);
|
||||
}, [redoChanges, udpateDbFlag, isProcessing, isLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { createContext } from "react";
|
||||
import useHotkeys from "@reecelucas/react-use-hotkeys";
|
||||
import { useDatabaseHistory } from "../database-history/database-history-provider";
|
||||
import { useReactFlow } from "@xyflow/react";
|
||||
import { useModal } from "../modal-provider/modal-provider";
|
||||
import { Modals } from "../modal-provider/modal-contxet";
|
||||
import { useDiagramOps } from "../diagram-provider/diagram-provider";
|
||||
|
||||
const DatabaseHotkeysContext = createContext<null>(null);
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const DatabaseHotkeysProvider: React.FC<Props> = ({ children }) => {
|
||||
const { canUndo, undo, canRedo, redo } = useDatabaseHistory();
|
||||
const { openController, showController } = useDiagramOps();
|
||||
const { fitView } = useReactFlow();
|
||||
const { open } = useModal();
|
||||
|
||||
useHotkeys(["Control+z", "Meta+z"], (event: KeyboardEvent) => {
|
||||
event.preventDefault()
|
||||
canUndo && undo();
|
||||
});
|
||||
|
||||
useHotkeys(["Control+Shift+z", "Meta+Shift+z", "Control+y"], (event: KeyboardEvent) => {
|
||||
event.preventDefault()
|
||||
canRedo && redo();
|
||||
});
|
||||
|
||||
useHotkeys(["Control+a", "Meta+a"], (event: KeyboardEvent) => {
|
||||
event.preventDefault()
|
||||
|
||||
fitView({
|
||||
duration: 500,
|
||||
padding: 0.1,
|
||||
maxZoom: 0.9,
|
||||
})
|
||||
});
|
||||
|
||||
useHotkeys(["Control+o", "Meta+o"], (event: KeyboardEvent) => {
|
||||
event.preventDefault()
|
||||
open(Modals.OPEN_DATABASE);
|
||||
});
|
||||
|
||||
|
||||
useHotkeys(["Control+b", "Meta+b"], (event: KeyboardEvent) => {
|
||||
event.preventDefault()
|
||||
openController(!showController)
|
||||
});
|
||||
|
||||
|
||||
return (
|
||||
<DatabaseHotkeysContext.Provider value={null}>
|
||||
{children}
|
||||
</DatabaseHotkeysContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatabaseHotkeysProvider;
|
||||
@@ -19,8 +19,8 @@ interface DatabaseDataContextType {
|
||||
currentDatabaseId: string | undefined,
|
||||
databases: DatabaseType[],
|
||||
isLoading: boolean,
|
||||
isFetching : boolean ,
|
||||
isSyncing : boolean ,
|
||||
|
||||
|
||||
isSwitchingDatabase: boolean,
|
||||
getField: (tableId: string, id: string) => FieldType | undefined,
|
||||
|
||||
@@ -52,7 +52,7 @@ interface DatabaseOperationsContextType {
|
||||
// index operations
|
||||
createIndex: (index: IndexInsertType) => Promise<QueryResult>,
|
||||
editIndex: (index: IndexInsertType) => Promise<QueryResult>,
|
||||
deleteIndex: (id: string) => Promise<QueryResult>,
|
||||
deleteIndex: (id: string) => Promise<void>,
|
||||
editFieldIndices: (indexId: string, fieldIds: string[]) => Promise<void>,
|
||||
// relationship operations
|
||||
createRelationship: (relationship: RelationshipInsertType) => Promise<QueryResult>,
|
||||
|
||||
@@ -16,24 +16,25 @@ import { IndexInsertType, indices } from "@/lib/schemas/index-schema";
|
||||
import { field_indices, FieldIndexInsertType } from "@/lib/schemas/field_index-schema";
|
||||
import { v4 } from "uuid";
|
||||
import { DataType } from "@/lib/schemas/data-type-schema";
|
||||
import { deleteFieldsWithCascade, deleteTablesWithCascade } from "@/utils/cascade";
|
||||
import { deleteFieldsWithCascade, deleteIndicesWithCascade, deleteTablesWithCascade } from "@/utils/cascade";
|
||||
import DatabaseHotkeysProvider from "../database-hotkeys/database-hotkeys-provider";
|
||||
|
||||
|
||||
|
||||
interface Props { children: React.ReactNode }
|
||||
|
||||
const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
const syncStatus = usePowerSync();
|
||||
|
||||
const [currentDatabaseId, setCurrentDatabaseId] = useState<string | undefined>(localStorage.getItem("database_id") as string | undefined);
|
||||
// Fetch all databases
|
||||
const { data: databases, isLoading: loadingDatabases, isFetching: fetchingDatabases } = useQuery(toCompilableQuery(
|
||||
const { data: databases, isLoading: loadingDatabases } = useQuery(toCompilableQuery(
|
||||
db.query.databases.findMany({
|
||||
orderBy: desc(databaseModel.createdAt)
|
||||
})
|
||||
));
|
||||
|
||||
// Fetch the current database with nested tables, fields, and relationships
|
||||
let { data: database, isLoading: loadingCurrentDatabase, isFetching: fetchingDatabase } = useQuery(
|
||||
let { data: database, isLoading: loadingCurrentDatabase } = useQuery(
|
||||
toCompilableQuery(
|
||||
db.query.databases.findMany({
|
||||
where: (databases, { eq }) => eq(databases.id, currentDatabaseId as string),
|
||||
@@ -86,9 +87,8 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
else
|
||||
database = undefined as any;
|
||||
|
||||
|
||||
// Fetch all data types
|
||||
let { data: data_types, isLoading: loadingDataTypes, isFetching: fetchingDatatypes } = useQuery(toCompilableQuery(
|
||||
let { data: data_types, isLoading: loadingDataTypes } = useQuery(toCompilableQuery(
|
||||
db.query.data_types.findMany({
|
||||
where: (data_types, { eq }) => eq(data_types.dialect, (database as any)?.dialect)
|
||||
})
|
||||
@@ -99,13 +99,10 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
}, [data_types]);
|
||||
|
||||
const isLoading: boolean = loadingDataTypes || loadingDatabases || loadingCurrentDatabase;
|
||||
const isFetching: boolean = fetchingDatabase || fetchingDatatypes || fetchingDatabases;
|
||||
|
||||
const isSyncing : boolean = isLoading ||isFetching || !(syncStatus.currentStatus as any).options.hasSynced ;
|
||||
|
||||
|
||||
const isSwitchingDatabase: boolean = useMemo(() => {
|
||||
return fetchingDatabase && currentDatabaseId != (database as any)?.id
|
||||
}, [currentDatabaseId, database, fetchingDatabase]);
|
||||
return isLoading && currentDatabaseId != (database as any)?.id
|
||||
}, [currentDatabaseId, database, isLoading]);
|
||||
|
||||
// CRUD for database
|
||||
const createDatabase = useCallback(async (database: DatabaseInsertType): Promise<QueryResult> => {
|
||||
@@ -164,7 +161,7 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
const deleteTable = useCallback(async (id: string): Promise<void> => {
|
||||
if (currentDatabaseId) {
|
||||
await db.transaction(async (tx) => {
|
||||
await deleteFieldsWithCascade([id], tx);
|
||||
await deleteTablesWithCascade([id], tx);
|
||||
await updateDbNumTables(currentDatabaseId, tx);
|
||||
});
|
||||
} else {
|
||||
@@ -228,8 +225,10 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
}, [db]);
|
||||
|
||||
|
||||
const deleteIndex = useCallback(async (id: string): Promise<QueryResult> => {
|
||||
return await db.delete(indices).where(eq(indices.id, id))
|
||||
const deleteIndex = useCallback(async (id: string): Promise<void> => {
|
||||
return db.transaction(async (tx) => {
|
||||
await deleteIndicesWithCascade([id], tx)
|
||||
})
|
||||
}, [db])
|
||||
|
||||
const editIndex = useCallback(async (index: IndexInsertType): Promise<QueryResult> => {
|
||||
@@ -299,7 +298,7 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
name: operation.chnages.name
|
||||
}).where(eq(databaseModel.id, currentDatabaseId)));
|
||||
}
|
||||
else if (operation.type == "UPDATE_NUM_TABLES") {
|
||||
else if (operation.type == "UPDATE_NUM_TABLES") {
|
||||
if (currentDatabaseId)
|
||||
operations.push(
|
||||
tx.update(databaseModel).set({
|
||||
@@ -369,7 +368,7 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
}
|
||||
else if (operation.type == "DELETE_INDEX") {
|
||||
operations.push(
|
||||
tx.delete(indices).where(eq(indices.id, operation.indexId))
|
||||
deleteIndicesWithCascade([operation.indexId ] , tx)
|
||||
);
|
||||
}
|
||||
else if (operation.type == "UPDATE_INDEX") {
|
||||
@@ -393,7 +392,7 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
return await Promise.all(operations);
|
||||
})
|
||||
} catch (error) {
|
||||
|
||||
|
||||
throw error
|
||||
}
|
||||
}, [db, currentDatabaseId]);
|
||||
@@ -512,13 +511,15 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
databases: databases as DatabaseType[],
|
||||
isLoading,
|
||||
isSwitchingDatabase,
|
||||
isFetching,
|
||||
|
||||
getField,
|
||||
isSyncing
|
||||
|
||||
}}>
|
||||
<DatabaseOperationsContext.Provider value={databaseOpsValue}>
|
||||
<DatabaseHistoryProvider>
|
||||
|
||||
{children}
|
||||
|
||||
</DatabaseHistoryProvider>
|
||||
</DatabaseOperationsContext.Provider>
|
||||
</DatabaseDataContext.Provider>
|
||||
|
||||
@@ -23,7 +23,7 @@ export const ModalProvider: React.FC<Props> = ({ children }) => {
|
||||
const [currentModal, setCurrentModal] = useState<CurrentModalProps | undefined>(undefined);
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const open = useCallback((modal: Modals, props?: any) => {
|
||||
|
||||
|
||||
setCurrentModal({
|
||||
modal,
|
||||
props
|
||||
|
||||
@@ -6,6 +6,15 @@ import { AppSchema, drizzleSchema } from '@/lib/schemas/app-schema';
|
||||
import { StackRenderConnector } from '@/utils/stackrender-connector';
|
||||
import { CircularProgress } from '@heroui/react';
|
||||
import { PowerSyncSQLiteDatabase, wrapPowerSyncWithDrizzle } from '@powersync/drizzle-driver';
|
||||
import { data_types, DataInsertType } from '@/lib/schemas/data-type-schema';
|
||||
import { MysqlDataType } from '@/lib/data_types/mysql_data_types';
|
||||
import { PostgresDataType } from '@/lib/data_types/postgres_data_types';
|
||||
import { SqliteDataTypes } from '@/lib/data_types/sqlite_data_types';
|
||||
import { MariaDbDataType } from '@/lib/data_types/mariadb_data_types';
|
||||
import { DatabaseDialect } from '@/lib/database';
|
||||
import { seedDataTypes } from '@/lib/data_types/seed_datatypes';
|
||||
|
||||
|
||||
|
||||
export const powerSyncDb = new PowerSyncDatabase({
|
||||
database: {
|
||||
@@ -17,9 +26,6 @@ export const powerSyncDb = new PowerSyncDatabase({
|
||||
export const db: PowerSyncSQLiteDatabase<typeof drizzleSchema> = wrapPowerSyncWithDrizzle(powerSyncDb, {
|
||||
schema: drizzleSchema,
|
||||
});
|
||||
|
||||
|
||||
|
||||
const ConnectorContext = createContext<StackRenderConnector | null>(null);
|
||||
export const useConnector = () => useContext(ConnectorContext);
|
||||
|
||||
@@ -30,26 +36,17 @@ interface SyncProviderProps {
|
||||
export const SyncProvider: React.FC<SyncProviderProps> = ({ children }) => {
|
||||
|
||||
const [powerSync] = useState(powerSyncDb);
|
||||
const [connector] = useState(new StackRenderConnector());
|
||||
|
||||
useEffect(() => {
|
||||
const setup = async () => {
|
||||
await powerSync.init();
|
||||
await powerSync.execute("PRAGMA foreign_keys = ON;");
|
||||
powerSync.connect(connector);
|
||||
};
|
||||
setup();
|
||||
}, [powerSync, connector]);
|
||||
|
||||
|
||||
|
||||
|
||||
const seed = async () => {
|
||||
await seedDataTypes(db) ;
|
||||
}
|
||||
seed();
|
||||
}, [db])
|
||||
return (
|
||||
<Suspense fallback={<CircularProgress />}>
|
||||
<PowerSyncContext.Provider value={powerSync}>
|
||||
<ConnectorContext.Provider value={connector}>
|
||||
{children}
|
||||
</ConnectorContext.Provider>
|
||||
{children}
|
||||
</PowerSyncContext.Provider>
|
||||
</Suspense>
|
||||
)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
|
||||
import { Routes } from 'react-router-dom';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import useDashboardRoutes from './dashboard-route';
|
||||
const useAppRoutes = () => {
|
||||
import NotFoundPage from '@/pages/not-found/not-found-page';
|
||||
const useAppRoutes = () => {
|
||||
const dashboardRoutes = useDashboardRoutes();
|
||||
return (
|
||||
<Routes>
|
||||
{dashboardRoutes}
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
|
||||
import DashboardPage from '@/pages/dashboard/dashboard-page';
|
||||
import { Route } from 'react-router-dom';
|
||||
import { Navigate, Route } from 'react-router-dom';
|
||||
import useDatabaseRoutes from './database-route';
|
||||
import NotFoundPage from '@/pages/not-found/not-found-page';
|
||||
|
||||
|
||||
|
||||
|
||||
const useDashboardRoutes = () => {
|
||||
const databaseRoutes = useDatabaseRoutes() ;
|
||||
const databaseRoutes = useDatabaseRoutes();
|
||||
return (
|
||||
<Route path="" element={<DashboardPage />}>
|
||||
<Route index element={<Navigate to="database" replace />} />
|
||||
{databaseRoutes}
|
||||
|
||||
</Route>
|
||||
|
||||
);
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
import DatabasePage from '@/pages/database/database-page';
|
||||
import RelationshipController from '@/pages/database/db-controller/relationship-controller/relationship-controller';
|
||||
import TablesController from '@/pages/database/db-controller/tables-controller/tables-controller';
|
||||
import { Route } from 'react-router-dom';
|
||||
import { Navigate, Route } from 'react-router-dom';
|
||||
|
||||
|
||||
const useDatabaseRoutes = () => {
|
||||
|
||||
return (
|
||||
<Route path="/database" element={<DatabasePage />} >
|
||||
<Route index element={<Navigate to="tables" replace />} />
|
||||
<Route path='tables' element={<TablesController />}></Route>
|
||||
<Route path='relationships' element={<RelationshipController />}></Route>
|
||||
<Route path='bot' element={<DatabasePage />}></Route>
|
||||
|
||||
</Route>
|
||||
);
|
||||
};
|
||||
|
||||
+2
-11
@@ -1,28 +1,19 @@
|
||||
import { fields, FieldType } from "@/lib/schemas/field-schema";
|
||||
import { field_indices } from "@/lib/schemas/field_index-schema";
|
||||
import { relationships } from "@/lib/schemas/relationship-schema";
|
||||
import { QueryResult } from "@powersync/web";
|
||||
import { eq, ExtractTablesWithRelations, inArray, or } from "drizzle-orm";
|
||||
import { SQLiteTransaction } from "drizzle-orm/sqlite-core";
|
||||
import { FieldType } from "@/lib/schemas/field-schema";
|
||||
|
||||
import { v4 } from "uuid";
|
||||
|
||||
|
||||
|
||||
|
||||
export const getNextSequence = (fields: FieldType[]): number => {
|
||||
|
||||
if (fields.length == 0)
|
||||
return 0;
|
||||
|
||||
const maxSequenceItem = fields.reduce((max, field: FieldType) => {
|
||||
return field.sequence > max.sequence ? field : max
|
||||
});
|
||||
return maxSequenceItem.sequence + 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export const cloneField = (field: FieldType): FieldType => {
|
||||
return {
|
||||
...field,
|
||||
|
||||
+130
-7
@@ -1,7 +1,10 @@
|
||||
import { ForeignKeyActions } from "@/lib/field"
|
||||
import { DatabaseType } from "@/lib/schemas/database-schema"
|
||||
import { FieldType } from "@/lib/schemas/field-schema"
|
||||
import { Cardinality, RelationshipType } from "@/lib/schemas/relationship-schema"
|
||||
import { TableType } from "@/lib/schemas/table-schema"
|
||||
|
||||
import { tablesRelations, TableType } from "@/lib/schemas/table-schema"
|
||||
import { v4 } from "uuid"
|
||||
import pluralize from 'pluralize';
|
||||
export const getRelationshipSourceAndTarget = (sourceTableId: string, sourceField: FieldType, targetTableId: string, targetField: FieldType) => {
|
||||
if (sourceField.isPrimary || targetField.isPrimary) {
|
||||
if (sourceField.isPrimary) {
|
||||
@@ -56,11 +59,11 @@ export const getForeignRelationships = (table: TableType): RelationshipType[] =>
|
||||
relationship.cardinality == Cardinality.many_to_one
|
||||
) : []).map((relationship: RelationshipType) => {
|
||||
return {
|
||||
...relationship ,
|
||||
sourceTable : relationship.targetTable ,
|
||||
targetTable : relationship.sourceTable ,
|
||||
sourceField : relationship.targetField ,
|
||||
targetField : relationship.sourceField ,
|
||||
...relationship,
|
||||
sourceTable: relationship.targetTable,
|
||||
targetTable: relationship.sourceTable,
|
||||
sourceField: relationship.targetField,
|
||||
targetField: relationship.sourceField,
|
||||
|
||||
}
|
||||
});
|
||||
@@ -78,4 +81,124 @@ export const getDefaultRelationshipName = (relationship: RelationshipType) => {
|
||||
if (!relationship.sourceTable || !relationship.targetTable || !relationship.sourceField || !relationship.targetField)
|
||||
return "";
|
||||
return `fk_${relationship.sourceTable?.name}_${relationship.targetTable?.name}`
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export const decomposeManyToMany = (database: DatabaseType): DatabaseType => {
|
||||
// Clone the tables and relationships to avoid mutating the original database object
|
||||
let relationships: RelationshipType[] = [...database.relationships];
|
||||
let tables: TableType[] = [...database.tables];
|
||||
|
||||
// Filter out all many-to-many relationships to process
|
||||
let manyToManyRelationships = relationships.filter(
|
||||
(relationship: RelationshipType) => relationship.cardinality == Cardinality.many_to_many
|
||||
);
|
||||
|
||||
// Loop through each many-to-many relationship
|
||||
for (const relationship of manyToManyRelationships) {
|
||||
// Find the source and target tables
|
||||
const sourceTable: TableType = tables.find(
|
||||
(table: TableType) => table.id == relationship.sourceTableId
|
||||
) as TableType;
|
||||
|
||||
const targetTable: TableType = tables.find(
|
||||
(table: TableType) => table.id == relationship.targetTableId
|
||||
) as TableType;
|
||||
|
||||
// Find the source and target fields involved in the relationship
|
||||
const sourceField: FieldType = sourceTable.fields.find(
|
||||
(field: FieldType) => field.id == relationship.sourceFieldId
|
||||
) as FieldType;
|
||||
|
||||
const targetField: FieldType = targetTable.fields.find(
|
||||
(field: FieldType) => field.id == relationship.targetFieldId
|
||||
) as FieldType;
|
||||
|
||||
// Convert table names to singular if they are plural
|
||||
const singularSourceTableName: string = pluralize.isPlural(sourceTable.name)
|
||||
? pluralize.singular(sourceTable.name)
|
||||
: sourceTable.name;
|
||||
|
||||
const singularTargetTableName: string = pluralize.isPlural(targetTable.name)
|
||||
? pluralize.singular(targetTable.name)
|
||||
: targetTable.name;
|
||||
|
||||
// Create a foreign key field pointing to the source table
|
||||
const junctionSourceField: FieldType = {
|
||||
id: v4(),
|
||||
name: `${singularSourceTableName}_${sourceField.name}`,
|
||||
nullable: false,
|
||||
typeId: sourceField.typeId,
|
||||
type: sourceField.type,
|
||||
isPrimary: true
|
||||
} as FieldType;
|
||||
|
||||
// Create a foreign key field pointing to the target table
|
||||
const junctionTargetField: FieldType = {
|
||||
id: v4(),
|
||||
name: `${singularTargetTableName}_${targetField.name}`,
|
||||
nullable: false,
|
||||
typeId: targetField.typeId,
|
||||
type: targetField.type,
|
||||
isPrimary: true
|
||||
} as FieldType;
|
||||
|
||||
// Define the new junction table combining both foreign keys
|
||||
const junctionTable: TableType = {
|
||||
id: v4(),
|
||||
name: `${singularSourceTableName}_${targetTable.name}`,
|
||||
fields: [
|
||||
junctionSourceField,
|
||||
junctionTargetField
|
||||
]
|
||||
} as TableType;
|
||||
|
||||
// Create a one-to-many relationship from the source table to the junction table
|
||||
const junctionSourceRelationship: RelationshipType = {
|
||||
id: v4(),
|
||||
sourceTable: sourceTable,
|
||||
targetTable: junctionTable,
|
||||
sourceTableId: sourceTable.id,
|
||||
targetTableId: junctionTable.id,
|
||||
|
||||
sourceField: sourceField,
|
||||
sourceFieldId: sourceField.id,
|
||||
targetField: junctionSourceField,
|
||||
targetFieldId: junctionSourceField.id,
|
||||
|
||||
cardinality: Cardinality.one_to_many,
|
||||
onDelete: ForeignKeyActions.CASCADE
|
||||
} as RelationshipType;
|
||||
|
||||
// Create a one-to-many relationship from the target table to the junction table
|
||||
const junctionTargetRelationship: RelationshipType = {
|
||||
id: v4(),
|
||||
sourceTable: targetTable,
|
||||
targetTable: junctionTable,
|
||||
sourceTableId: targetTable.id,
|
||||
targetTableId: junctionTable.id,
|
||||
|
||||
sourceField: targetField,
|
||||
sourceFieldId: targetField.id,
|
||||
targetField: junctionTargetField,
|
||||
targetFieldId: junctionTargetField.id,
|
||||
|
||||
cardinality: Cardinality.one_to_many,
|
||||
onDelete: ForeignKeyActions.CASCADE
|
||||
} as RelationshipType;
|
||||
|
||||
// Add the new junction table and the two relationships to the database
|
||||
tables.push(junctionTable);
|
||||
relationships.push(junctionSourceRelationship);
|
||||
relationships.push(junctionTargetRelationship);
|
||||
}
|
||||
|
||||
// Return the updated database object with the new junction tables and relationships added
|
||||
return {
|
||||
...database,
|
||||
tables,
|
||||
relationships
|
||||
};
|
||||
}
|
||||
@@ -21,6 +21,7 @@ export const DatabaseToAst = (database: DatabaseType, data_types: DataType[]) =>
|
||||
|
||||
let renderableTables: RenderableTable[] = database.tables.map((table: TableType) => toRenderableTable(table, database));
|
||||
let sortableTables: SortableTable[] = renderableTables.map((table: RenderableTable) => toSortableTable(table));
|
||||
|
||||
try {
|
||||
const sortedTablesIds: string[] = orderTables(sortableTables);
|
||||
|
||||
@@ -32,8 +33,10 @@ export const DatabaseToAst = (database: DatabaseType, data_types: DataType[]) =>
|
||||
if (database.dialect == DatabaseDialect.POSTGRES) {
|
||||
postgresEnums = table.fields.filter((field: FieldType) => field.type?.name == "enum");
|
||||
if (postgresEnums.length > 0) {
|
||||
for (const postgresEnum of postgresEnums)
|
||||
for (const postgresEnum of postgresEnums) {
|
||||
(postgresEnum as any).table = table;
|
||||
dbAst.push(PotgresEnumToAst(postgresEnum));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,17 +44,16 @@ export const DatabaseToAst = (database: DatabaseType, data_types: DataType[]) =>
|
||||
|
||||
if (table.indices && table.indices.length > 0)
|
||||
for (const index of table.indices)
|
||||
dbAst.push(IndexToAst({
|
||||
...index,
|
||||
fields: index.fieldIndices.map((fieldIndex: FieldIndexType) =>
|
||||
table.fields.find((field: FieldType) => field.id == fieldIndex.fieldId)
|
||||
) as FieldType[],
|
||||
}, table));
|
||||
if (index.fieldIndices?.length > 0)
|
||||
dbAst.push(IndexToAst({
|
||||
...index,
|
||||
fields: index.fieldIndices.map((fieldIndex: FieldIndexType) =>
|
||||
table.fields.find((field: FieldType) => field.id == fieldIndex.fieldId)
|
||||
) as FieldType[],
|
||||
}, table));
|
||||
};
|
||||
} catch (error) {
|
||||
|
||||
throw error;
|
||||
|
||||
}
|
||||
return dbAst;
|
||||
}
|
||||
@@ -78,20 +80,17 @@ export const TableToAst = (table: TableType, data_types: DataType[], isPostgresD
|
||||
...foreignKeysConstraints
|
||||
];
|
||||
|
||||
const filed_definitions = table.fields.map((field: FieldType) => {
|
||||
const filed_definitions = table.fields.filter((field: FieldType) => field.type).map((field: FieldType) => {
|
||||
const isPostgresEnum: boolean = isPostgresDialect && field.type.name == "enum";
|
||||
|
||||
return FieldToAst({
|
||||
...field,
|
||||
type: !(isPostgresEnum) ?
|
||||
data_types.find((dataType: DataType) => dataType.id == field.typeId) as DataType :
|
||||
{ name: `${field.name.toLowerCase()}_enum` } as DataType,
|
||||
{ name: `${table.name}_${field.name.toLowerCase()}_enum` } as DataType,
|
||||
}, multiPrimaryKeys, !isPostgresEnum)
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
return {
|
||||
keyword: "table",
|
||||
type: "create",
|
||||
@@ -286,7 +285,7 @@ export const PotgresEnumToAst = (field: FieldType) => {
|
||||
resource: "enum",
|
||||
name: {
|
||||
schema: null,
|
||||
name: `${field.name.toLowerCase()}_enum`
|
||||
name: `${(field as any).table.name}_${field.name.toLowerCase()}_enum`
|
||||
},
|
||||
keyword: "type",
|
||||
create_definitions: {
|
||||
@@ -340,7 +339,7 @@ export const relationshipToAst = (relationship: RelationshipType) => {
|
||||
|
||||
if (relationship.onDelete) {
|
||||
const value: string | null = foreignKeyActionToAst(relationship.onDelete as ForeignKeyActions);
|
||||
|
||||
|
||||
if (value)
|
||||
on_action.push({
|
||||
type: "on delete",
|
||||
@@ -363,7 +362,7 @@ export const relationshipToAst = (relationship: RelationshipType) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
constraint: null,
|
||||
definition: [
|
||||
@@ -388,7 +387,7 @@ export const relationshipToAst = (relationship: RelationshipType) => {
|
||||
}
|
||||
],
|
||||
keyword: "references",
|
||||
on_action
|
||||
on_action
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,6 @@ import { DatabaseDialect, getDatabaseByDialect } from "@/lib/database";
|
||||
import { randomColor } from "@/lib/colors";
|
||||
import { Cardinality, RelationshipInsertType } from "@/lib/schemas/relationship-schema";
|
||||
import { IndexInsertType } from "@/lib/schemas/index-schema";
|
||||
import { relationshipToAst } from "./database_to_ast";
|
||||
import { DatabaseInsertType } from "@/lib/schemas/database-schema";
|
||||
|
||||
|
||||
export const SqlToDatabase = (sql: string, data_types: DataType[], dialect: DatabaseDialect) => {
|
||||
@@ -89,9 +87,9 @@ export const SqlToDatabase = (sql: string, data_types: DataType[], dialect: Data
|
||||
{ name: column.name.name }
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
const relationshipAst: any = foreignKeyConstraintToAlterTableAst([referenceColumn], table);
|
||||
|
||||
|
||||
try {
|
||||
const newRelationships = postgresAstToRelationship(relationshipAst, tables);
|
||||
relationships = relationships.concat(newRelationships);
|
||||
@@ -104,7 +102,7 @@ export const SqlToDatabase = (sql: string, data_types: DataType[], dialect: Data
|
||||
}
|
||||
|
||||
if ((instructionAst[0] as any).constraints && (instructionAst[0] as any).constraints.length > 0) {
|
||||
|
||||
|
||||
const relationshipAst: any = foreignKeyConstraintToAlterTableAst((instructionAst[0] as any).constraints, table)
|
||||
try {
|
||||
const newRelationships = postgresAstToRelationship(relationshipAst, tables);
|
||||
@@ -206,15 +204,16 @@ export const SqlToDatabase = (sql: string, data_types: DataType[], dialect: Data
|
||||
|
||||
for (const alterTable of alterTableStatements) {
|
||||
try {
|
||||
const instructionAst = parser.astify(alterTable, {
|
||||
let instructionAst: any = parser.astify(alterTable, {
|
||||
database: getDatabaseByDialect(dialect).name
|
||||
});
|
||||
|
||||
if (instructionAst) {
|
||||
const extractedRelationships: RelationshipInsertType[] = astToRelationship(tables, undefined, undefined, {
|
||||
...instructionAst,
|
||||
table: (instructionAst as any).table?.[0].table
|
||||
...instructionAst[0],
|
||||
table: instructionAst[0].table?.[0].table
|
||||
}) as RelationshipInsertType[];
|
||||
|
||||
|
||||
relationships = relationships.concat(extractedRelationships)
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -330,7 +329,7 @@ export const astToRelationship = (tables: TableInsertType[], constraintAst?: any
|
||||
const sourceField: FieldInsertType | undefined = sourceTable?.fields?.find((field: FieldInsertType) => field.name == expression.create_definitions?.reference_definition?.definition?.[0].column);
|
||||
|
||||
const on_action: any | undefined = expression.create_definitions?.reference_definition?.on_action;
|
||||
|
||||
|
||||
let onDelete: ForeignKeyActions | undefined;
|
||||
let onUpdate: ForeignKeyActions | undefined;
|
||||
if (on_action) {
|
||||
@@ -515,6 +514,7 @@ export const postgresAstToField = (ast: any, data_types: DataType[], sequence: n
|
||||
});
|
||||
autoIncrement = true;
|
||||
}
|
||||
|
||||
const modifiers: string[] = dataType?.modifiers ? JSON.parse(dataType.modifiers) : [];
|
||||
|
||||
let length: number | undefined;
|
||||
@@ -546,12 +546,17 @@ export const postgresAstToField = (ast: any, data_types: DataType[], sequence: n
|
||||
const constraints: any[] | undefined = ast.constraints;
|
||||
if (constraints && constraints.length > 0) {
|
||||
|
||||
|
||||
const nullableConstraints: any | undefined = constraints.find((c: any) => c.type == "not null");
|
||||
const defaultValueConstraints: any | undefined = constraints.find((c: any) => c.type == "default");
|
||||
|
||||
if (defaultValueConstraints) {
|
||||
console.log(defaultValueConstraints.default)
|
||||
if (defaultValueConstraints.default.type == "keyword" && defaultValueConstraints.default.keyword == "current_timestamp")
|
||||
defaultValue = TimeDefaultValues.NOW;
|
||||
|
||||
else if (defaultValueConstraints.default.type == "call" && defaultValueConstraints.default.function?.name == "now")
|
||||
defaultValue = TimeDefaultValues.NOW;
|
||||
|
||||
else if (defaultValueConstraints.default.type == "cast" && defaultValueConstraints.default.operand)
|
||||
defaultValue = String(defaultValueConstraints.default.operand.value)
|
||||
else
|
||||
@@ -589,11 +594,8 @@ export const postgresAstToField = (ast: any, data_types: DataType[], sequence: n
|
||||
} as FieldInsertType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const postgresAstToRelationship = (ast: any, tables: TableInsertType[]): RelationshipInsertType[] => {
|
||||
|
||||
|
||||
const relationships: RelationshipInsertType[] = [];
|
||||
const changes = ast.changes;
|
||||
|
||||
@@ -606,18 +608,13 @@ export const postgresAstToRelationship = (ast: any, tables: TableInsertType[]):
|
||||
|
||||
for (const foreignKeyConstraint of foreignKeyConstraints) {
|
||||
|
||||
|
||||
const sourceTable: TableInsertType | undefined = tables.find((table: TableInsertType) => table.name == foreignKeyConstraint.foreignTable.name);
|
||||
|
||||
const targetField: FieldInsertType | undefined = targetTable.fields?.find((field: FieldInsertType) => field.name == foreignKeyConstraint.localColumns[0]?.name)
|
||||
|
||||
const sourceField: FieldInsertType | undefined = sourceTable?.fields?.find((field: FieldInsertType) => field.name == foreignKeyConstraint.foreignColumns[0]?.name)
|
||||
|
||||
const onDelete = foreignKeyConstraint.onDelete ? astToForiegnKeyAction( foreignKeyConstraint.onDelete) : undefined ;
|
||||
const onUpdate = foreignKeyConstraint.onUpdate ? astToForiegnKeyAction( foreignKeyConstraint.onUpdate) : undefined ;
|
||||
|
||||
|
||||
|
||||
const onDelete = foreignKeyConstraint.onDelete ? astToForiegnKeyAction(foreignKeyConstraint.onDelete) : undefined;
|
||||
const onUpdate = foreignKeyConstraint.onUpdate ? astToForiegnKeyAction(foreignKeyConstraint.onUpdate) : undefined;
|
||||
|
||||
if (!sourceField || !targetField || !sourceTable)
|
||||
continue;
|
||||
|
||||
@@ -627,12 +624,11 @@ export const postgresAstToRelationship = (ast: any, tables: TableInsertType[]):
|
||||
targetTableId: targetTable.id,
|
||||
sourceFieldId: sourceField.id,
|
||||
targetFieldId: targetField.id,
|
||||
cardinality: targetField.unique ? Cardinality.one_to_one : Cardinality.one_to_many ,
|
||||
onDelete , onUpdate
|
||||
cardinality: targetField.unique ? Cardinality.one_to_one : Cardinality.one_to_many,
|
||||
onDelete, onUpdate
|
||||
} as RelationshipInsertType)
|
||||
}
|
||||
|
||||
|
||||
if (relationships.length == foreignKeyConstraints.length)
|
||||
return relationships;
|
||||
else
|
||||
@@ -641,8 +637,6 @@ export const postgresAstToRelationship = (ast: any, tables: TableInsertType[]):
|
||||
message: "Failed to Extract all relationships",
|
||||
relationships
|
||||
} as any)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -664,9 +658,9 @@ const foreignKeyConstraintToAlterTableAst = (constraints: any[], table: TableIns
|
||||
{
|
||||
name: constraint.foreignColumns?.[0].name
|
||||
}
|
||||
],
|
||||
onDelete : constraint.onDelete ,
|
||||
onUpdate : constraint.onUpdate ,
|
||||
],
|
||||
onDelete: constraint.onDelete,
|
||||
onUpdate: constraint.onUpdate,
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -677,7 +671,6 @@ const foreignKeyConstraintToAlterTableAst = (constraints: any[], table: TableIns
|
||||
name: table.name
|
||||
},
|
||||
changes
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,12 +681,9 @@ export const astToIndex = (ast: any, tables: TableInsertType[]): IndexInsertType
|
||||
throw Error("table not found");
|
||||
|
||||
const fieldNames: string[] = ast.index_columns.map((column: any) => column.column);
|
||||
|
||||
|
||||
const fieldIds: string[] | undefined = table.fields?.filter((field: FieldInsertType) => fieldNames.includes(field.name))
|
||||
.map((field: FieldInsertType) => field.id);
|
||||
|
||||
|
||||
return {
|
||||
id: v4(),
|
||||
name: ast.index,
|
||||
|
||||
@@ -86,10 +86,10 @@ export function fixSQLiteColumnOrder(sql: string): string {
|
||||
const lines = sql.split('\n');
|
||||
let currentColumn = '';
|
||||
const result: string[] = [];
|
||||
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
|
||||
|
||||
if (isTableStructureLine(trimmed)) {
|
||||
if (currentColumn) {
|
||||
result.push(processSQLiteIntegerColumn(currentColumn));
|
||||
@@ -98,7 +98,7 @@ export function fixSQLiteColumnOrder(sql: string): string {
|
||||
result.push(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (trimmed.endsWith(',')) {
|
||||
currentColumn += ' ' + trimmed.slice(0, -1);
|
||||
result.push(processSQLiteIntegerColumn(currentColumn) + ',');
|
||||
@@ -107,7 +107,7 @@ export function fixSQLiteColumnOrder(sql: string): string {
|
||||
currentColumn += ' ' + trimmed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (currentColumn) result.push(processSQLiteIntegerColumn(currentColumn));
|
||||
return result.join('\n');
|
||||
}
|
||||
@@ -123,7 +123,7 @@ function processSQLiteIntegerColumn(columnDef: string): string {
|
||||
const isPrimaryKey = rest.match(/\bPRIMARY\s+KEY\b/i);
|
||||
const isAutoIncrement = rest.match(/\bAUTOINCREMENT\b/i);
|
||||
const isNotNull = rest.match(/\bNOT\s+NULL\b/i);
|
||||
|
||||
|
||||
if (!isPrimaryKey && !isAutoIncrement) {
|
||||
return columnDef; // Leave non-PK INTEGER columns unchanged
|
||||
}
|
||||
@@ -138,7 +138,7 @@ function processSQLiteIntegerColumn(columnDef: string): string {
|
||||
|
||||
// Reconstruct with SQLite's required order
|
||||
let reconstructed = `${colName} INTEGER`;
|
||||
|
||||
|
||||
if (isPrimaryKey) reconstructed += ' PRIMARY KEY';
|
||||
if (isAutoIncrement) reconstructed += ' AUTOINCREMENT';
|
||||
if (isNotNull) reconstructed += ' NOT NULL';
|
||||
@@ -148,7 +148,7 @@ function processSQLiteIntegerColumn(columnDef: string): string {
|
||||
}
|
||||
|
||||
export interface CircularDependencyError {
|
||||
cycle : string[] ;
|
||||
success : boolean ;
|
||||
message : string ;
|
||||
cycle: string[];
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
Reference in New Issue
Block a user