mirror of
https://github.com/stackrender/stackrender.git
synced 2026-09-10 11:15:42 +00:00
Column Modifiers for Sqlite and Mysql done and Sql Rendering Improvments
This commit is contained in:
@@ -7,8 +7,9 @@ import { useEffect, useState } from "react";
|
||||
import { Parser } from "node-sql-parser";
|
||||
import { DatabaseType } from "@/lib/schemas/database-schema";
|
||||
import { format } from 'sql-formatter';
|
||||
import { getDatabaseByDialect } from "@/lib/database";
|
||||
import { fixCharsetPlacement } from "@/utils/render/parsers/render-uttils";
|
||||
import { DatabaseDialect, getDatabaseByDialect } from "@/lib/database";
|
||||
import { CircularDependencyError, fixCharsetPlacement, fixSQLiteColumnOrder } from "@/utils/render/parsers/render-uttils";
|
||||
import { areArraysEqual } from "@/utils/utils";
|
||||
|
||||
const parser = new Parser();
|
||||
|
||||
@@ -16,24 +17,35 @@ 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)
|
||||
useEffect(() => {
|
||||
try {
|
||||
const dbAst: any = DatabaseToAst(database, data_types);
|
||||
const sql = fixCharsetPlacement(format(parser.sqlify(dbAst, {
|
||||
let sql: string = parser.sqlify(dbAst, {
|
||||
database: getDatabaseByDialect(database.dialect).name
|
||||
}), { language: "sql" } ));
|
||||
|
||||
});
|
||||
if (database.dialect != DatabaseDialect.SQLITE)
|
||||
sql = fixCharsetPlacement(format(sql, { language: "sql" }));
|
||||
else
|
||||
sql = fixSQLiteColumnOrder(format(sql, { language: "sql" }));
|
||||
|
||||
const formattedSqlCode = format(sql, { language: 'sql' });
|
||||
|
||||
setSql(
|
||||
(formattedSqlCode)
|
||||
);
|
||||
setCircularDependency( undefined ) ;
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
|
||||
setCircularDependency((previousError) => {
|
||||
if ( !previousError || !areArraysEqual( previousError.cycle , (error as CircularDependencyError).cycle) )
|
||||
return error as CircularDependencyError ;
|
||||
return previousError ;
|
||||
})
|
||||
|
||||
}
|
||||
}, [database]);
|
||||
|
||||
return sql;
|
||||
return {sql , circularDependency};
|
||||
|
||||
}
|
||||
@@ -111,6 +111,13 @@ export const en = {
|
||||
invalid_relationship: {
|
||||
title: "Invalid Relationship",
|
||||
description: "The source key type does not match the referenced key type. Please ensure both keys have the same data type."
|
||||
} ,
|
||||
circular_dependency : {
|
||||
title : "Circular Dependency Detected" ,
|
||||
toast_description : "A circular reference between tables was found. Check the diagram on the left and remove one of the relationships to fix it." ,
|
||||
description : "Your schema contains a circular foreign key relationship between tables. To fix it" ,
|
||||
suggestion : "remove one of the relationships listed below that are causing the cycle." ,
|
||||
remove_relationship : "Remove relationship"
|
||||
}
|
||||
},
|
||||
table: {
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
|
||||
|
||||
|
||||
|
||||
export interface DatabaseType {
|
||||
name: string,
|
||||
dialect: string;
|
||||
logo: string
|
||||
}
|
||||
|
||||
|
||||
|
||||
export enum DatabaseDialect {
|
||||
MYSQL = "mysql",
|
||||
POSTGRES = "postgres",
|
||||
|
||||
@@ -98,7 +98,8 @@ const DatabasePage: React.FC = () => {
|
||||
addToast({
|
||||
title: t("db_controller.invalid_relationship.title"),
|
||||
description: t("db_controller.invalid_relationship.description"),
|
||||
color: "danger",
|
||||
color: "danger",
|
||||
variant : "solid"
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/tooltip/tooltip";
|
||||
import { RelationshipType } from "@/lib/schemas/relationship-schema";
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { useDiagramOps } from "@/providers/diagram-provider/diagram-provider";
|
||||
import { CircularDependencyError } from "@/utils/render/parsers/render-uttils";
|
||||
import { addToast, Alert, Button, Listbox, ListboxItem, toast } from "@heroui/react";
|
||||
import { AlertTriangle, Trash } from "lucide-react";
|
||||
import React, { useCallback, useEffect, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface CircularDependencyAlertProps {
|
||||
error: CircularDependencyError
|
||||
}
|
||||
const CircularDependencyAlert: React.FC<CircularDependencyAlertProps> = ({ error }) => {
|
||||
|
||||
const { database } = useDatabase() || { relationships: [] };
|
||||
const { relationships } = database || { relationships: [] }
|
||||
const { focusOnRelationship } = useDiagramOps();
|
||||
const { deleteRelationship } = useDatabaseOperations();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const circularRelationships: RelationshipType[] = useMemo(() => {
|
||||
|
||||
let cycle: RelationshipType[] = [];
|
||||
|
||||
for (let index = 0; index < error.cycle?.length - 1; index++) {
|
||||
const sourceTableId: string = error.cycle[index];
|
||||
const targetTableId: string = error.cycle[index + 1];
|
||||
const relationship: RelationshipType | undefined = relationships.find((relationship: RelationshipType) => relationship.sourceTableId == sourceTableId && relationship.targetTableId == targetTableId)
|
||||
if (relationship)
|
||||
cycle.push(
|
||||
relationship
|
||||
)
|
||||
}
|
||||
|
||||
return cycle;
|
||||
|
||||
}, [error, relationships])
|
||||
|
||||
|
||||
const focus = (key: any) => {
|
||||
console.log(key)
|
||||
focusOnRelationship(
|
||||
key, true, false
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const removeRelationship = useCallback((id: string) => {
|
||||
deleteRelationship(id)
|
||||
}, [])
|
||||
return (
|
||||
<div className="flex flex-col gap-2 w-full h-full items-center pt-12 ">
|
||||
<AlertTriangle
|
||||
className="size-12 text-danger"
|
||||
/>
|
||||
<h3 className="text-danger font-semibold">
|
||||
{t("db_controller.circular_dependency.title")}
|
||||
</h3>
|
||||
<p className="text-sm text-font/70 text-center w-[80%] max-w-[360px]">
|
||||
{t("db_controller.circular_dependency.description")} , <span className="font-semibold text-font/90"> {t("db_controller.circular_dependency.suggestion")} </span>
|
||||
</p>
|
||||
|
||||
<Listbox aria-label="Relationships" className="w-[80%] max-w-[360px]" onAction={focus}
|
||||
|
||||
|
||||
|
||||
>
|
||||
{
|
||||
circularRelationships.map((relationship: RelationshipType) => (
|
||||
<ListboxItem
|
||||
variant={"faded"}
|
||||
className="data-[hover=true]:bg-default-500"
|
||||
key={relationship.id}>
|
||||
<div className="flex items-center justify-between tex-font/90">
|
||||
<span>
|
||||
{relationship.sourceTable.name} -> {relationship.targetTable.name}
|
||||
</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
<Button
|
||||
isIconOnly
|
||||
variant="light"
|
||||
color={"danger"}
|
||||
size="sm"
|
||||
onPress={() => removeRelationship(relationship.id)}
|
||||
>
|
||||
<Trash className="text-danger size-4" />
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{t("db_controller.circular_dependency.remove_relationship")}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</ListboxItem>
|
||||
))
|
||||
}
|
||||
</Listbox>
|
||||
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
export default React.memo(CircularDependencyAlert)
|
||||
@@ -6,13 +6,12 @@ import CodeMirror, { EditorView } from '@uiw/react-codemirror';
|
||||
import { sql } from '@codemirror/lang-sql';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
import { useTheme } from "next-themes";
|
||||
import { Parser } from "node-sql-parser";
|
||||
|
||||
import { overrideDarkTheme, overrideLightTheme } from "@/lib/colors";
|
||||
import { DatabaseType } from "@/lib/schemas/database-schema";
|
||||
const parser = new Parser();
|
||||
const code = `
|
||||
|
||||
`
|
||||
import CircularDependencyAlert from "./circular-dependecy-alert";
|
||||
import { addToast } from "@heroui/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
|
||||
|
||||
@@ -20,30 +19,36 @@ const code = `
|
||||
const SqlPreview: React.FC = ({ }) => {
|
||||
|
||||
const { database } = useDatabase();
|
||||
const sqlCode = useRenderSql(database as DatabaseType);
|
||||
|
||||
const { sql: sqlCode, circularDependency } = useRenderSql(database as DatabaseType);
|
||||
const { resolvedTheme } = useTheme();
|
||||
const { t } = useTranslation() ;
|
||||
|
||||
useEffect(() => {
|
||||
const ast = parser.astify(code, {
|
||||
database: "Postgresql"
|
||||
}) ;
|
||||
|
||||
console.log(ast)
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
if (circularDependency)
|
||||
addToast({
|
||||
title: t("db_controller.circular_dependency.title"),
|
||||
description: t("db_controller.circular_dependency.description") ,
|
||||
color: "danger",
|
||||
variant: "solid"
|
||||
});
|
||||
}, [circularDependency])
|
||||
|
||||
return (
|
||||
<div className="flex w-full h-full ">
|
||||
{
|
||||
|
||||
<CodeMirror
|
||||
value={sqlCode}
|
||||
className="flex flex-1 w-full"
|
||||
extensions={[sql()]}
|
||||
theme={resolvedTheme == "light" ? overrideLightTheme : [oneDark, overrideDarkTheme]}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
if (circularDependency)
|
||||
return <CircularDependencyAlert error={circularDependency} />
|
||||
else
|
||||
return (
|
||||
<div className="flex w-full h-full ">
|
||||
{
|
||||
<CodeMirror
|
||||
value={sqlCode}
|
||||
className="flex flex-1 w-full"
|
||||
extensions={[sql()]}
|
||||
theme={resolvedTheme == "light" ? overrideLightTheme : [oneDark, overrideDarkTheme]}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ const TablesController: React.FC<Props> = ({ }) => {
|
||||
name: `table_${tables.length + 1}`,
|
||||
posX,
|
||||
posY,
|
||||
|
||||
fields: [{
|
||||
id: v4(),
|
||||
name: "id",
|
||||
|
||||
@@ -373,10 +373,13 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
|
||||
|
||||
const getDefaultPrimaryKeyType = useCallback((dialect?: DatabaseDialect) => {
|
||||
if (!dialect || dialect != DatabaseDialect.POSTGRES)
|
||||
return data_types.find((dataType: DataType) => dataType.name == "bigint");
|
||||
else if (dialect == DatabaseDialect.POSTGRES)
|
||||
if (dialect == DatabaseDialect.POSTGRES)
|
||||
return data_types.find((dataType: DataType) => dataType.name == "bigserial");
|
||||
else if (dialect == DatabaseDialect.SQLITE)
|
||||
return data_types.find((dataType: DataType) => dataType.name == "integer");
|
||||
|
||||
return data_types.find((dataType: DataType) => dataType.name == "bigint");
|
||||
|
||||
}, [data_types]);
|
||||
|
||||
const databaseOpsValue = useMemo(() => ({
|
||||
|
||||
@@ -8,7 +8,7 @@ interface DiagramDataContextType {
|
||||
|
||||
interface DiagramOpsContextType {
|
||||
focusOnTable: (id: string, transition?: boolean) => void,
|
||||
focusOnRelationship: (id: string, transition?: boolean) => void,
|
||||
focusOnRelationship: (id: string, transition?: boolean , withNavigate? : boolean) => void,
|
||||
setIsConnectionInProgress: Dispatch<boolean>,
|
||||
isConnectionInProgress: boolean
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useContext, useMemo, useState } from "react";
|
||||
import { useCallback, useContext, useMemo, useState } from "react";
|
||||
|
||||
import { useReactFlow } from "@xyflow/react";
|
||||
import { useReactFlow } from "@xyflow/react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { RelationshipType } from "@/lib/schemas/relationship-schema";
|
||||
import { DiagramDataContext, DiagramOpsContext } from "./diagram-context";
|
||||
@@ -43,8 +43,10 @@ const DiagramProvider: React.FC<Props> = ({ children }) => {
|
||||
}, [setFocusedTableId])
|
||||
|
||||
|
||||
const focusOnRelationship = useCallback((id: string, transition: boolean = false) => {
|
||||
navigate("/database/relationships");
|
||||
const focusOnRelationship = useCallback((id: string, transition: boolean = false, withNavigate: boolean = true) => {
|
||||
if (withNavigate)
|
||||
navigate("/database/relationships");
|
||||
|
||||
setFocusedRelationshipId(id);
|
||||
|
||||
setEdges((edges) =>
|
||||
|
||||
@@ -27,7 +27,16 @@ export const DatabaseToAst = (database: DatabaseType, data_types: DataType[]) =>
|
||||
renderableTables.find((table: TableType) => table.id == id) as TableType
|
||||
);
|
||||
for (const table of sortedTables) {
|
||||
dbAst.push(TableToAst(table, data_types));
|
||||
let postgresEnums: FieldType[];
|
||||
if (database.dialect == DatabaseDialect.POSTGRES) {
|
||||
postgresEnums = table.fields.filter((field: FieldType) => field.type?.name == "enum");
|
||||
if (postgresEnums.length > 0) {
|
||||
for (const postgresEnum of postgresEnums)
|
||||
dbAst.push(PotgresEnumToAst(postgresEnum));
|
||||
}
|
||||
}
|
||||
|
||||
dbAst.push(TableToAst(table, data_types, database.dialect == DatabaseDialect.POSTGRES));
|
||||
|
||||
if (table.indices && table.indices.length > 0)
|
||||
for (const index of table.indices)
|
||||
@@ -39,16 +48,18 @@ export const DatabaseToAst = (database: DatabaseType, data_types: DataType[]) =>
|
||||
}, table));
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error ;
|
||||
|
||||
}
|
||||
return dbAst;
|
||||
}
|
||||
|
||||
|
||||
export const TableToAst = (table: TableType, data_types: DataType[]) => {
|
||||
export const TableToAst = (table: TableType, data_types: DataType[], isPostgresDialect: boolean = false) => {
|
||||
|
||||
const primaryKeys: FieldType[] = table.fields.filter((field: FieldType) => field.isPrimary);
|
||||
const multiPrimaryKeys: boolean = primaryKeys.length > 1;
|
||||
|
||||
const primaryKeysConstraints: any[] | undefined = multiPrimaryKeys ? [{
|
||||
constraint_type: "primary key",
|
||||
resource: "constraint",
|
||||
@@ -59,13 +70,26 @@ export const TableToAst = (table: TableType, data_types: DataType[]) => {
|
||||
}] : [];
|
||||
|
||||
let foreignRelationships = getForeignRelationships(table);
|
||||
|
||||
const foreignKeysConstraints = foreignRelationships.map((relationship: RelationshipType) => relationshipToAst(relationship));
|
||||
const constraints: any[] = [...primaryKeysConstraints, ...foreignKeysConstraints];
|
||||
const filed_definitions = table.fields.map((field: FieldType) => FieldToAst({
|
||||
...field,
|
||||
type: data_types.find((dataType: DataType) => dataType.id == field.typeId) as DataType,
|
||||
}, multiPrimaryKeys));
|
||||
const constraints: any[] = [
|
||||
...primaryKeysConstraints,
|
||||
...foreignKeysConstraints
|
||||
];
|
||||
|
||||
const filed_definitions = table.fields.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,
|
||||
}, multiPrimaryKeys , !isPostgresEnum)
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
return {
|
||||
keyword: "table",
|
||||
type: "create",
|
||||
@@ -75,10 +99,11 @@ export const TableToAst = (table: TableType, data_types: DataType[]) => {
|
||||
create_definitions: [...filed_definitions, ...constraints]
|
||||
}
|
||||
}
|
||||
export const FieldToAst = (field: FieldType, ignorePrimaryKey: boolean = false) => {
|
||||
export const FieldToAst = (field: FieldType, ignorePrimaryKey: boolean = false, upperCaseType: boolean = true) => {
|
||||
|
||||
const modifiers: string[] = field?.type.modifiers ? JSON.parse(field?.type.modifiers) : [];
|
||||
let suffix: string[] = [];
|
||||
|
||||
if (modifiers.includes(Modifiers.ZEROFILL) && field.zeroFill) {
|
||||
suffix.push(Modifiers.ZEROFILL.toUpperCase());
|
||||
}
|
||||
@@ -97,8 +122,7 @@ export const FieldToAst = (field: FieldType, ignorePrimaryKey: boolean = false)
|
||||
let valuesExpr: any | null;
|
||||
|
||||
let default_val: any | null = null;
|
||||
let dataType : string | undefined = field.type?.name?.toLocaleUpperCase() ;
|
||||
|
||||
let dataType: string | undefined = upperCaseType ? field.type?.name?.toLocaleUpperCase() : field.type?.name as string | undefined ;
|
||||
|
||||
if (modifiers.includes(Modifiers.PRECISION) && field.precision) {
|
||||
length = field.precision;
|
||||
@@ -111,8 +135,9 @@ export const FieldToAst = (field: FieldType, ignorePrimaryKey: boolean = false)
|
||||
|
||||
if (modifiers.includes(Modifiers.LENGTH) && field.maxLength)
|
||||
length = field.maxLength;
|
||||
else if (modifiers.includes(Modifiers.LENGTH) && field.type.dialect == DatabaseDialect.MYSQL && field.type.name?.startsWith('var') && !field.maxLength)
|
||||
length = MYSQL_MAX_VAR_LENGTH ;
|
||||
|
||||
else if (modifiers.includes(Modifiers.LENGTH) && (field.type.dialect == DatabaseDialect.MYSQL || field.type.dialect == DatabaseDialect.MARIADB) && field.type.name?.startsWith('var') && !field.maxLength)
|
||||
length = MYSQL_MAX_VAR_LENGTH;
|
||||
|
||||
if (modifiers.includes(Modifiers.CHARSET) && field.charset)
|
||||
character_set = {
|
||||
@@ -186,9 +211,6 @@ export const FieldToAst = (field: FieldType, ignorePrimaryKey: boolean = false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// field.type?.name == "varchar" && (field.type?.dialect == DatabaseDialect.MYSQL || field.type?.dialect == DatabaseDialect.MARIADB) ? 255 : null
|
||||
return {
|
||||
column: {
|
||||
@@ -206,11 +228,11 @@ export const FieldToAst = (field: FieldType, ignorePrimaryKey: boolean = false)
|
||||
auto_increment: (modifiers.includes(Modifiers.AUTO_INCREMENT) && field.autoIncrement && field.type.dialect != DatabaseDialect.POSTGRES) ? "auto_increment" : null,
|
||||
|
||||
nullable: {
|
||||
type: field.nullable ? "null" : "not null",
|
||||
type: field.nullable ? "null" : "not null",
|
||||
value: field.nullable ? "null" : "not null",
|
||||
},
|
||||
definition: {
|
||||
dataType ,
|
||||
dataType,
|
||||
length,
|
||||
scale,
|
||||
suffix,
|
||||
@@ -239,6 +261,29 @@ export const IndexToAst = (index: IndexType, table: TableType) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const PotgresEnumToAst = (field: FieldType) => {
|
||||
|
||||
const jsonValues = field.values ? JSON.parse(field.values) : [];
|
||||
|
||||
return {
|
||||
as : "as" ,
|
||||
type: "create",
|
||||
resource: "enum",
|
||||
name: {
|
||||
schema: null,
|
||||
name: `${field.name.toLowerCase()}_enum`
|
||||
},
|
||||
keyword: "type",
|
||||
create_definitions: {
|
||||
parentheses: true,
|
||||
type: "expr_list",
|
||||
value: jsonValues.map((value: string) => ({
|
||||
type: "single_quote_string",
|
||||
value
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -79,4 +79,76 @@ function processColumn(columnDef: string): string {
|
||||
if (cleanRest) reconstructed += ` ${cleanRest}`;
|
||||
|
||||
return reconstructed;
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
currentColumn = '';
|
||||
}
|
||||
result.push(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trimmed.endsWith(',')) {
|
||||
currentColumn += ' ' + trimmed.slice(0, -1);
|
||||
result.push(processSQLiteIntegerColumn(currentColumn) + ',');
|
||||
currentColumn = '';
|
||||
} else {
|
||||
currentColumn += ' ' + trimmed;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentColumn) result.push(processSQLiteIntegerColumn(currentColumn));
|
||||
return result.join('\n');
|
||||
}
|
||||
|
||||
function processSQLiteIntegerColumn(columnDef: string): string {
|
||||
// Only process INTEGER columns with PRIMARY KEY and/or AUTOINCREMENT
|
||||
const integerPkMatch = columnDef.match(/^\s*(\w+)\s+INTEGER\s+(.*)/i);
|
||||
if (!integerPkMatch) return columnDef;
|
||||
|
||||
const [_, colName, rest] = integerPkMatch;
|
||||
|
||||
// Check if this is a PRIMARY KEY column
|
||||
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
|
||||
}
|
||||
|
||||
// Clean the remaining attributes
|
||||
let cleanRest = rest
|
||||
.replace(/\bPRIMARY\s+KEY\b/gi, '')
|
||||
.replace(/\bAUTOINCREMENT\b/gi, '')
|
||||
.replace(/\bNOT\s+NULL\b/gi, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
|
||||
// Reconstruct with SQLite's required order
|
||||
let reconstructed = `${colName} INTEGER`;
|
||||
|
||||
if (isPrimaryKey) reconstructed += ' PRIMARY KEY';
|
||||
if (isAutoIncrement) reconstructed += ' AUTOINCREMENT';
|
||||
if (isNotNull) reconstructed += ' NOT NULL';
|
||||
if (cleanRest) reconstructed += ` ${cleanRest}`;
|
||||
|
||||
return reconstructed.trim();
|
||||
}
|
||||
|
||||
export interface CircularDependencyError {
|
||||
cycle : string[] ;
|
||||
success : boolean ;
|
||||
message : string ;
|
||||
}
|
||||
Reference in New Issue
Block a user