mirror of
https://github.com/stackrender/stackrender.git
synced 2026-09-10 19:25:44 +00:00
fix charset and collation order whene rendering a text column declaration sql
This commit is contained in:
@@ -19,8 +19,7 @@ const RenameDatabase: React.FC<RenameDatabaseProps> = ({ database}) => {
|
||||
const { t } = useTranslation();
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
console.log (database.name) ;
|
||||
useEffect(() => {
|
||||
setDbName(database.name);
|
||||
}, [database.name])
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ const TagInput: React.FC<TagInputProps> = ({ defaultItems = [], onItemsChange })
|
||||
tags={tags}
|
||||
classNames={{
|
||||
tag: "font-normal inline-block m-0.5 border-1 border-default rounded-full px-2 py-1 flex-row ",
|
||||
remove: "bg-green-500 bg-default-900 rounded-full text-xs text-center ml-1 min-w-[15px] max-w-[15px] min-h-[15px] max-h-[15px] transition-colors duration-300 hover:bg-black",
|
||||
remove: " bg-default-900 rounded-full text-xs text-center ml-1 min-w-[15px] max-w-[15px] min-h-[15px] max-h-[15px] transition-colors duration-300 hover:bg-black",
|
||||
tagInputField: "relative w-full inline-flex flex-row items-center bg-default-100 border-1 border-divider hover:border-primary focus-within:border-default-400 h-8 min-h-8 px-2 rounded-small transition-background !duration-150 transition-colors outline-none dark:bg-default placeholder:text-foreground-500 mt-2" ,
|
||||
tags : "max-h-[256px] overflow-auto "
|
||||
}}
|
||||
|
||||
@@ -7,7 +7,8 @@ import { useEffect, useState } from "react";
|
||||
import { Parser } from "node-sql-parser";
|
||||
import { DatabaseType } from "@/lib/schemas/database-schema";
|
||||
import { format } from 'sql-formatter';
|
||||
import { DatabaseDialect, getDatabaseByDialect } from "@/lib/database";
|
||||
import { getDatabaseByDialect } from "@/lib/database";
|
||||
import { fixCharsetPlacement } from "@/utils/render/parsers/render-uttils";
|
||||
|
||||
const parser = new Parser();
|
||||
|
||||
@@ -17,18 +18,22 @@ export const useRenderSql = (database: DatabaseType) => {
|
||||
const { data_types } = useDatabaseOperations();
|
||||
|
||||
useEffect(() => {
|
||||
const dbAst: any = DatabaseToAst(database, data_types);
|
||||
|
||||
const formattedSqlCode = format(parser.sqlify(dbAst, {
|
||||
database: getDatabaseByDialect(database.dialect ).name
|
||||
}), { language: 'sql' });
|
||||
setSql(
|
||||
formattedSqlCode
|
||||
);
|
||||
try {
|
||||
const dbAst: any = DatabaseToAst(database, data_types);
|
||||
const sql = fixCharsetPlacement(format(parser.sqlify(dbAst, {
|
||||
database: getDatabaseByDialect(database.dialect).name
|
||||
}), { language: "sql" } ));
|
||||
|
||||
const formattedSqlCode = format(sql, { language: 'sql' });
|
||||
|
||||
setSql(
|
||||
(formattedSqlCode)
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}, [database]);
|
||||
|
||||
|
||||
return sql;
|
||||
|
||||
}
|
||||
@@ -6,33 +6,16 @@ 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 { Parser } from "node-sql-parser";
|
||||
import { overrideDarkTheme, overrideLightTheme } from "@/lib/colors";
|
||||
import { DatabaseType } from "@/lib/schemas/database-schema";
|
||||
const parser = new Parser();
|
||||
const code = `
|
||||
|
||||
CREATE TABLE \`users\` (
|
||||
id BIGINT NOT NULL PRIMARY KEY UNIQUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
lastname VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
profile_url VARCHAR(255),
|
||||
age SMALLINT,
|
||||
birthday DATE,
|
||||
created_at TIMESTAMP NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE \`addresses\` (
|
||||
id BIGINT NOT NULL PRIMARY KEY UNIQUE,
|
||||
street_line VARCHAR(255) NOT NULL,
|
||||
latitude FLOAT,
|
||||
longitude FLOAT, -- fixed spelling
|
||||
state_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
FOREIGN KEY (\`user_id\`) REFERENCES \`users\` (\`id\`)
|
||||
);
|
||||
|
||||
username VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT "test" ,
|
||||
username VARCHAR(100) NOT NULL DEFAULT "test" CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
|
||||
|
||||
)
|
||||
`
|
||||
|
||||
|
||||
@@ -41,14 +24,14 @@ CREATE TABLE \`addresses\` (
|
||||
const SqlPreview: React.FC = ({ }) => {
|
||||
|
||||
const { database } = useDatabase();
|
||||
const sqlCode = useRenderSql(database);
|
||||
const sqlCode = useRenderSql(database as DatabaseType);
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
const ast = parser.astify(code, {
|
||||
database: "Mysql"
|
||||
})
|
||||
console.log(ast)
|
||||
// console.log(ast)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
@@ -59,7 +42,7 @@ const SqlPreview: React.FC = ({ }) => {
|
||||
value={sqlCode}
|
||||
className="flex flex-1 w-full"
|
||||
extensions={[sql()]}
|
||||
theme={resolvedTheme == "light" ? overrideLightTheme : [oneDark , overrideDarkTheme] }
|
||||
theme={resolvedTheme == "light" ? overrideLightTheme : [oneDark, overrideDarkTheme]}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
+1
-4
@@ -85,7 +85,6 @@ const FieldItem: React.FC<Props> = ({ field }) => {
|
||||
placeholder={t("db_controller.name")}
|
||||
value={fieldName}
|
||||
onValueChange={setFieldName}
|
||||
|
||||
onBlur={saveFieldName}
|
||||
classNames={{
|
||||
inputWrapper: "border-divider group-hover:border-primary",
|
||||
@@ -109,8 +108,6 @@ const FieldItem: React.FC<Props> = ({ field }) => {
|
||||
>
|
||||
N
|
||||
</ToggleButton>
|
||||
|
||||
|
||||
<ToggleButton
|
||||
onToggle={togglePrimaryKey}
|
||||
active={field.isPrimary as boolean}
|
||||
@@ -119,7 +116,7 @@ const FieldItem: React.FC<Props> = ({ field }) => {
|
||||
<KeyRound className="size-4" />
|
||||
</ToggleButton>
|
||||
|
||||
<Popover placement="right-start" radius="sm" shadow="sm" isOpen={popOverOpen} onOpenChange={setPopOverOpen} >
|
||||
<Popover placement="bottom" radius="sm" shadow="sm" isOpen={popOverOpen} onOpenChange={setPopOverOpen} >
|
||||
<PopoverTrigger>
|
||||
<Button
|
||||
size="sm"
|
||||
|
||||
+42
-17
@@ -1,10 +1,11 @@
|
||||
import TagInput from "@/components/tag-input/tag-input";
|
||||
import { Modifiers, PostgreSQLCharset, PostgreSQLCollation } from "@/lib/field";
|
||||
import { DatabaseDialect } from "@/lib/database";
|
||||
import { Modifiers, MySQLCharset, MySQLCollation, PostgreSQLCharset, PostgreSQLCollation, SQLiteCharset, SQLiteCollation } from "@/lib/field";
|
||||
import { FieldInsertType, FieldType } from "@/lib/schemas/field-schema";
|
||||
import { useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { Button, Checkbox, Input, Select, SelectItem, SharedSelection, Switch, Textarea } from "@heroui/react";
|
||||
import { Trash2 } from "lucide-react";
|
||||
import React, { Ref, useCallback, useRef, useState } from "react";
|
||||
import React, { Ref, useCallback, useMemo, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
|
||||
@@ -18,8 +19,7 @@ interface FieldSettingProps {
|
||||
|
||||
const FieldSetting: React.FC<FieldSettingProps> = ({ field }) => {
|
||||
|
||||
console.log(field.type)
|
||||
const modifiers: string[] = field.type.modifiers ? JSON.parse(field.type.modifiers) : [];
|
||||
const modifiers: string[] = field.type?.modifiers ? JSON.parse(field.type.modifiers) : [];
|
||||
|
||||
const { deleteField, editField } = useDatabaseOperations();
|
||||
const { t } = useTranslation();
|
||||
@@ -32,9 +32,34 @@ const FieldSetting: React.FC<FieldSettingProps> = ({ field }) => {
|
||||
const scaleRef: Ref<HTMLInputElement> = useRef<HTMLInputElement>(null);
|
||||
const precisionRef: Ref<HTMLInputElement> = useRef<HTMLInputElement>(null);
|
||||
|
||||
const collations = useMemo(() => {
|
||||
if (!field.type)
|
||||
return undefined;
|
||||
|
||||
if (field.type.dialect == DatabaseDialect.MYSQL || field.type.dialect == DatabaseDialect.MARIADB)
|
||||
return MySQLCollation;
|
||||
if (field.type.dialect == DatabaseDialect.POSTGRES)
|
||||
return PostgreSQLCollation;
|
||||
if (field.type.dialect == DatabaseDialect.SQLITE)
|
||||
return SQLiteCollation;
|
||||
|
||||
}, [field]);
|
||||
const charsets = useMemo(() => {
|
||||
if (!field.type)
|
||||
return undefined;
|
||||
|
||||
if (field.type.dialect == DatabaseDialect.MYSQL || field.type.dialect == DatabaseDialect.MARIADB)
|
||||
return MySQLCharset;
|
||||
if (field.type.dialect == DatabaseDialect.POSTGRES)
|
||||
return PostgreSQLCharset;
|
||||
if (field.type.dialect == DatabaseDialect.SQLITE)
|
||||
return SQLiteCharset;
|
||||
|
||||
}, [field])
|
||||
const removeField = () => {
|
||||
deleteField(field.id)
|
||||
}
|
||||
|
||||
|
||||
const updateFieldNote = useCallback(() => {
|
||||
editField({
|
||||
@@ -73,29 +98,29 @@ const FieldSetting: React.FC<FieldSettingProps> = ({ field }) => {
|
||||
|
||||
|
||||
const changeCharset = useCallback((keys: SharedSelection) => {
|
||||
|
||||
|
||||
if (keys.anchorKey != field.charset) {
|
||||
|
||||
|
||||
editField({
|
||||
id: field.id,
|
||||
charset: keys.anchorKey,
|
||||
|
||||
} as FieldInsertType);
|
||||
}
|
||||
}
|
||||
setCharset(keys as any);
|
||||
}, [field]);
|
||||
|
||||
|
||||
const changeCollation = useCallback((keys: SharedSelection) => {
|
||||
|
||||
if (keys.anchorKey != field.charset) {
|
||||
|
||||
|
||||
if (keys.anchorKey != field.collate) {
|
||||
|
||||
editField({
|
||||
id: field.id,
|
||||
collate: keys.anchorKey,
|
||||
|
||||
} as FieldInsertType);
|
||||
}
|
||||
}
|
||||
setCollation(keys as any);
|
||||
}, [field])
|
||||
|
||||
@@ -142,7 +167,7 @@ const FieldSetting: React.FC<FieldSettingProps> = ({ field }) => {
|
||||
|
||||
const updateValues = useCallback((values: string[]) => {
|
||||
const jsonValues = JSON.stringify(values);
|
||||
console.log(jsonValues)
|
||||
|
||||
if (jsonValues != field.values)
|
||||
editField({
|
||||
id: field.id,
|
||||
@@ -155,7 +180,7 @@ const FieldSetting: React.FC<FieldSettingProps> = ({ field }) => {
|
||||
const showDecimalModifiers: boolean = modifiers.includes(Modifiers.PRECISION) || modifiers.includes(Modifiers.SCALE);
|
||||
const showTextModifiers: boolean = modifiers.includes(Modifiers.COLLATE) || modifiers.includes(Modifiers.CHARSET);
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-2 p-2 min-w-[260px] max-w-[260px]">
|
||||
@@ -273,7 +298,7 @@ const FieldSetting: React.FC<FieldSettingProps> = ({ field }) => {
|
||||
</h3>
|
||||
<hr className="border-divider" />
|
||||
{
|
||||
modifiers.includes(Modifiers.CHARSET) && <>
|
||||
modifiers.includes(Modifiers.CHARSET) && charsets && <>
|
||||
<label className="text-xs font-medium text-icon dark:text-font/90">
|
||||
{t("db_controller.field_settings.charset")}
|
||||
</label>
|
||||
@@ -290,13 +315,13 @@ const FieldSetting: React.FC<FieldSettingProps> = ({ field }) => {
|
||||
}}
|
||||
>
|
||||
{
|
||||
Object.values(PostgreSQLCharset).map((charset: string) => (<SelectItem key={charset}>{charset}</SelectItem>))
|
||||
Object.values(charsets).map((charset: string) => (<SelectItem key={charset}>{charset}</SelectItem>))
|
||||
}
|
||||
</Select>
|
||||
</>
|
||||
}
|
||||
{
|
||||
modifiers.includes(Modifiers.COLLATE) && <>
|
||||
modifiers.includes(Modifiers.COLLATE) && collations && <>
|
||||
<label className="text-xs font-medium text-icon dark:text-font/90">
|
||||
{t("db_controller.field_settings.collation")}
|
||||
</label>
|
||||
@@ -313,7 +338,7 @@ const FieldSetting: React.FC<FieldSettingProps> = ({ field }) => {
|
||||
}}
|
||||
>
|
||||
{
|
||||
Object.values(PostgreSQLCollation).map((collation: string) => (<SelectItem key={collation}>{collation}</SelectItem>))
|
||||
Object.values(collations).map((collation: string) => (<SelectItem key={collation}>{collation}</SelectItem>))
|
||||
}
|
||||
</Select>
|
||||
</>
|
||||
|
||||
@@ -63,8 +63,7 @@ const DatabaseHistoryProvider: React.FC<Props> = ({ children }) => {
|
||||
const operations: DBDiffOperation[] = mapDiffToDBDiffOperation(differences);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
console.log(operations)
|
||||
try {
|
||||
await executeDbDiffOps(operations)
|
||||
setIsProcessing(false);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { IndexInsertType, indices } from "@/lib/schemas/index-schema";
|
||||
import { field_indices } from "@/lib/schemas/field_index-schema";
|
||||
import { v4 } from "uuid";
|
||||
import { DataType } from "@/lib/schemas/data-type-schema";
|
||||
import { Modifiers } from "@/lib/field";
|
||||
|
||||
|
||||
|
||||
@@ -81,12 +82,23 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
else
|
||||
database = undefined as any;
|
||||
|
||||
// Fetch all data types
|
||||
// Fetch all data types
|
||||
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)
|
||||
})
|
||||
));
|
||||
|
||||
|
||||
const charsetOrCollationDataTypes = data_types.filter((dataType : DataType) => {
|
||||
const modifiers : string[] | undefined = dataType.modifiers ? JSON.parse(dataType.modifiers) : undefined ;
|
||||
if ( modifiers) {
|
||||
return modifiers.includes(Modifiers.COLLATE || Modifiers.CHARSET)
|
||||
}
|
||||
}).map((dataType : DataType) => dataType.name?.toUpperCase()) ;
|
||||
|
||||
|
||||
|
||||
|
||||
const grouped_data_types: any = useMemo(() => {
|
||||
return groupBy(data_types, "type");
|
||||
@@ -98,7 +110,6 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
if (databases.length > 0 && !currentDatabaseId) {
|
||||
switchDatabase(databases[0].id);
|
||||
}
|
||||
|
||||
}, [currentDatabaseId, databases]);
|
||||
|
||||
const isLoading: boolean = loadingDataTypes || loadingDatabases || loadingCurrentDatabase;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
|
||||
import { DatabaseDialect } from "@/lib/database";
|
||||
import { Modifiers } from "@/lib/field";
|
||||
import { DataType } from "@/lib/schemas/data-type-schema";
|
||||
import { DatabaseType } from "@/lib/schemas/database-schema";
|
||||
import { FieldType } from "@/lib/schemas/field-schema";
|
||||
@@ -41,7 +42,7 @@ export const DatabaseToAst = (database: DatabaseType, data_types: DataType[]) =>
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
return dbAst;
|
||||
}
|
||||
|
||||
@@ -77,26 +78,119 @@ export const TableToAst = (table: TableType, data_types: DataType[]) => {
|
||||
}
|
||||
}
|
||||
export const FieldToAst = (field: FieldType, ignorePrimaryKey: boolean = false) => {
|
||||
|
||||
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());
|
||||
}
|
||||
if (modifiers.includes(Modifiers.UNSIGNED) && field.unsigned) {
|
||||
suffix.push(Modifiers.UNSIGNED.toUpperCase());
|
||||
}
|
||||
|
||||
|
||||
let length: number | null = null;
|
||||
let scale: number | null = null;
|
||||
|
||||
let character_set: any | null = null;
|
||||
let collate: any | null = null;
|
||||
|
||||
let values: any[] = [];
|
||||
let valuesExpr: any | null;
|
||||
|
||||
let default_val: any | null = null;
|
||||
|
||||
if (modifiers.includes(Modifiers.PRECISION) && field.precision)
|
||||
length = field.precision;
|
||||
|
||||
if (modifiers.includes(Modifiers.SCALE) && field.scale)
|
||||
scale = field.scale;
|
||||
|
||||
if (modifiers.includes(Modifiers.LENGTH) && field.maxLength)
|
||||
length = field.maxLength;
|
||||
|
||||
|
||||
if (modifiers.includes(Modifiers.CHARSET) && field.charset)
|
||||
character_set = {
|
||||
type: "CHARACTER SET",
|
||||
value: {
|
||||
type: "default",
|
||||
value: field.charset
|
||||
}
|
||||
}
|
||||
|
||||
if (modifiers.includes(Modifiers.COLLATE) && field.collate)
|
||||
collate = {
|
||||
keyword: "collate",
|
||||
type: "collate",
|
||||
collate: {
|
||||
name: field.collate,
|
||||
}
|
||||
}
|
||||
|
||||
if (modifiers.includes(Modifiers.VALUES) && field.values) {
|
||||
|
||||
const jsonValues = JSON.parse(field.values);
|
||||
if (jsonValues.length > 0) {
|
||||
values = jsonValues.map((value: string) => ({
|
||||
type: "single_quote_string",
|
||||
value
|
||||
}));
|
||||
|
||||
valuesExpr = {
|
||||
parentheses: true,
|
||||
type: "expr_list",
|
||||
value: values
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (field.defaultValue && field.defaultValue.trim().length > 0) {
|
||||
default_val = {
|
||||
type: "default",
|
||||
value: {
|
||||
type: "single_quote_string",
|
||||
value: field.defaultValue
|
||||
}
|
||||
}
|
||||
if (field.defaultValue == "true" || field.defaultValue == "false") {
|
||||
default_val.value.type = "bool"
|
||||
default_val.value.value = Boolean(field.defaultValue);
|
||||
}
|
||||
|
||||
// Check if it's a number (but not empty string or just whitespace)
|
||||
if (!isNaN(Number(field.defaultValue))) {
|
||||
default_val.value.type = "number"
|
||||
default_val.value.value = Number(field.defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
// field.type?.name == "varchar" && (field.type?.dialect == DatabaseDialect.MYSQL || field.type?.dialect == DatabaseDialect.MARIADB) ? 255 : null
|
||||
return {
|
||||
column: {
|
||||
type: "column_ref",
|
||||
column: {
|
||||
expr: {
|
||||
type: "default", value: field.name
|
||||
type: "default", value: field.name,
|
||||
}
|
||||
},
|
||||
},
|
||||
default_val: null,
|
||||
collate,
|
||||
character_set,
|
||||
default_val,
|
||||
unique: field.unique ? "unique" : null,
|
||||
auto_increment: (modifiers.includes(Modifiers.AUTO_INCREMENT) && field.autoIncrement) ? "auto_increment" : null,
|
||||
|
||||
nullable: {
|
||||
type: field.nullable ? "null" : "not null",
|
||||
value: field.nullable ? "null" : "not null",
|
||||
},
|
||||
definition: {
|
||||
dataType: field.type?.name?.toLocaleUpperCase(),
|
||||
length: field.type?.name == "varchar" && (field.type?.dialect == DatabaseDialect.MYSQL || field.type?.dialect == DatabaseDialect.MARIADB) ? 255 : null,
|
||||
|
||||
|
||||
length,
|
||||
scale,
|
||||
suffix,
|
||||
expr: valuesExpr
|
||||
},
|
||||
primary_key: field.isPrimary && !ignorePrimaryKey ? "primary key" : null,
|
||||
resource: "column"
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Fixes charset/collation placement in SQL CREATE TABLE statements
|
||||
* @param sql The SQL string to process
|
||||
* @returns SQL with properly placed charset/collation clauses
|
||||
*/
|
||||
export function fixCharsetPlacement(sql: string): string {
|
||||
// Split into lines to handle multi-line cases better
|
||||
const lines = sql.split('\n');
|
||||
let currentColumn = '';
|
||||
const result: string[] = [];
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
|
||||
// Handle table start/end and other non-column lines
|
||||
if (isTableStructureLine(trimmed)) {
|
||||
if (currentColumn) {
|
||||
result.push(processColumn(currentColumn));
|
||||
currentColumn = '';
|
||||
}
|
||||
result.push(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle column definitions
|
||||
if (trimmed.endsWith(',')) {
|
||||
currentColumn += ' ' + trimmed.slice(0, -1);
|
||||
result.push(processColumn(currentColumn) + ',');
|
||||
currentColumn = '';
|
||||
} else {
|
||||
currentColumn += ' ' + trimmed;
|
||||
}
|
||||
}
|
||||
|
||||
// Process any remaining column
|
||||
if (currentColumn) {
|
||||
result.push(processColumn(currentColumn));
|
||||
}
|
||||
|
||||
return result.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a line is part of table structure (not a column definition)
|
||||
*/
|
||||
function isTableStructureLine(line: string): boolean {
|
||||
return line.startsWith('CREATE TABLE') ||
|
||||
line === '(' ||
|
||||
line === ')' ||
|
||||
line.endsWith('(') ||
|
||||
line.endsWith(')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes individual column definitions to fix charset/collation placement
|
||||
*/
|
||||
|
||||
function processColumn(columnDef: string): string {
|
||||
// Extract column name and data type
|
||||
const typeMatch = columnDef.match(/^\s*(\w+)\s+(\w+(?:\([^)]*\))?)/i);
|
||||
if (!typeMatch) return columnDef;
|
||||
|
||||
const [_, colName, dataType] = typeMatch;
|
||||
const rest = columnDef.slice(typeMatch[0].length);
|
||||
|
||||
// Extract charset and collate
|
||||
const charsetMatch = rest.match(/CHARACTER\s+SET\s+\S+/i);
|
||||
const collateMatch = rest.match(/COLLATE\s+\S+/i);
|
||||
|
||||
// Clean the remaining attributes
|
||||
const cleanRest = rest
|
||||
.replace(/CHARACTER\s+SET\s+\S+/gi, '')
|
||||
.replace(/COLLATE\s+\S+/gi, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
|
||||
// Reconstruct in correct order
|
||||
let reconstructed = `${colName} ${dataType}`;
|
||||
if (charsetMatch) reconstructed += ` ${charsetMatch[0]}`;
|
||||
if (collateMatch) reconstructed += ` ${collateMatch[0]}`;
|
||||
if (cleanRest) reconstructed += ` ${cleanRest}`;
|
||||
|
||||
return reconstructed;
|
||||
}
|
||||
+1
-2
@@ -40,8 +40,7 @@ const adjustTablesPositions = async (
|
||||
sources: [rel.sourceTableId],
|
||||
targets: [rel.targetTableId],
|
||||
})),
|
||||
};
|
||||
console.log(graph.children);
|
||||
};
|
||||
// Run ELK layout (async)
|
||||
const layoutedGraph = await elk.layout(graph);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user