mirror of
https://github.com/stackrender/stackrender.git
synced 2026-09-10 19:25:44 +00:00
Import Mysql from Sql feature implemented
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import { Modifiers, TimeDefaultValues } from "@/lib/field";
|
||||
import { DataTypes, Modifiers, TimeDefaultValues } from "@/lib/field";
|
||||
import { DataType } from "@/lib/schemas/data-type-schema";
|
||||
import { FieldInsertType, FieldType } from "@/lib/schemas/field-schema";
|
||||
import { TableInsertType } from "@/lib/schemas/table-schema";
|
||||
import { Parser } from "node-sql-parser";
|
||||
import { v4 } from "uuid";
|
||||
import { parse } from 'pgsql-ast-parser';
|
||||
import { DatabaseDialect } from "@/lib/database";
|
||||
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";
|
||||
@@ -15,6 +15,7 @@ import { DatabaseInsertType } from "@/lib/schemas/database-schema";
|
||||
|
||||
export const SqlToDatabase = (sql: string, data_types: DataType[], dialect: DatabaseDialect) => {
|
||||
const parser = new Parser();
|
||||
|
||||
const createTableStatements: string[] = [];
|
||||
const alterTableStatements: string[] = [];
|
||||
const createIndexStatements: string[] = [];
|
||||
@@ -52,7 +53,8 @@ export const SqlToDatabase = (sql: string, data_types: DataType[], dialect: Data
|
||||
}
|
||||
}
|
||||
|
||||
if (dialect == DatabaseDialect.POSTGRES)
|
||||
|
||||
if (dialect == DatabaseDialect.POSTGRES) {
|
||||
for (const postgresType of createPostgresTypesStatements) {
|
||||
try {
|
||||
const instructionAst = parse(postgresType);
|
||||
@@ -60,11 +62,11 @@ export const SqlToDatabase = (sql: string, data_types: DataType[], dialect: Data
|
||||
postgresTypes.push(instructionAst[0]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (dialect == DatabaseDialect.POSTGRES)
|
||||
|
||||
for (const createTable of createTableStatements) {
|
||||
try {
|
||||
const instructionAst = parse(createTable);
|
||||
@@ -74,7 +76,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 = astToRelationship(relationshipAst, tables);
|
||||
const newRelationships = postgresAstToRelationship(relationshipAst, tables);
|
||||
relationships = relationships.concat(newRelationships);
|
||||
} catch (error) {
|
||||
if ((error as any).relationships && (error as any).relationships.length > 0)
|
||||
@@ -83,35 +85,104 @@ export const SqlToDatabase = (sql: string, data_types: DataType[], dialect: Data
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
continue;
|
||||
|
||||
}
|
||||
}
|
||||
for (const createIndex of createIndexStatements) {
|
||||
try {
|
||||
const instructionAst = parse(createIndex);
|
||||
if (Array.isArray(instructionAst) && instructionAst.length > 0) {
|
||||
indices.push(postgresAstToIndex(instructionAst[0], tables));
|
||||
}
|
||||
} catch (error) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (const alterTable of alterTableStatements) {
|
||||
try {
|
||||
const instructionAst = parse(alterTable);
|
||||
if (Array.isArray(instructionAst) && instructionAst.length > 0) {
|
||||
const extractedRelationships: RelationshipInsertType[] = postgresAstToRelationship((instructionAst[0] as any), tables);
|
||||
relationships = relationships.concat(extractedRelationships);
|
||||
}
|
||||
} catch (error) {
|
||||
if ((error as any).relationships && (error as any).relationships.length > 0)
|
||||
relationships = relationships.concat((error as any).relationships);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const createTable of createTableStatements) {
|
||||
|
||||
try {
|
||||
const instructionAst = parser.astify(createTable, {
|
||||
database: getDatabaseByDialect(dialect).name
|
||||
});
|
||||
|
||||
if (Array.isArray(instructionAst) && instructionAst.length > 0) {
|
||||
console.log(instructionAst[0])
|
||||
const table: TableInsertType = astToTable(instructionAst[0], data_types);
|
||||
|
||||
tables.push(table);
|
||||
|
||||
const foreignKeyConstraints = (instructionAst[0] as any).create_definitions.filter((definition: any) => definition.constraint_type == "FOREIGN KEY");
|
||||
const referenceDefinitions = (instructionAst[0] as any).create_definitions.filter((definition: any) => definition.resource == "column" && definition.reference_definition);
|
||||
|
||||
for (const foreignKeyConstraint of foreignKeyConstraints) {
|
||||
relationships.push(astToRelationship(tables, table, foreignKeyConstraint) as RelationshipInsertType);
|
||||
}
|
||||
for (const referenceDefinition of referenceDefinitions) {
|
||||
relationships.push(astToRelationship(tables, table, undefined, referenceDefinition) as RelationshipInsertType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// console.log (error)
|
||||
continue;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (const createIndex of createIndexStatements) {
|
||||
try {
|
||||
const instructionAst = parse(createIndex);
|
||||
if (Array.isArray(instructionAst) && instructionAst.length > 0) {
|
||||
indices.push(astToIndex(instructionAst[0], tables));
|
||||
for (const alterTable of alterTableStatements) {
|
||||
try {
|
||||
const instructionAst = parser.astify(alterTable, {
|
||||
database: getDatabaseByDialect(dialect).name
|
||||
});
|
||||
if (instructionAst) {
|
||||
const targetTable: TableInsertType | undefined = tables.find((table: TableInsertType) => table.name == (instructionAst as any).table?.[0].table)
|
||||
const extractedRelationships: RelationshipInsertType[] = astToRelationship(tables, targetTable, undefined, undefined, instructionAst) as RelationshipInsertType[];
|
||||
|
||||
relationships = relationships.concat(extractedRelationships)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
if ((error as any).relationships && (error as any).relationships.length > 0)
|
||||
relationships = relationships.concat((error as any).relationships);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (const createIndex of createIndexStatements) {
|
||||
try {
|
||||
let instructionAst = parser.astify(createIndex, {
|
||||
database: getDatabaseByDialect(dialect).name
|
||||
});
|
||||
if (Array.isArray(instructionAst) && instructionAst.length > 0) {
|
||||
instructionAst = instructionAst[0];
|
||||
}
|
||||
indices.push(astToIndex(instructionAst, tables));
|
||||
|
||||
} catch (error) {
|
||||
continue;
|
||||
}
|
||||
} catch (error) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (const alterTable of alterTableStatements) {
|
||||
try {
|
||||
const instructionAst = parse(alterTable);
|
||||
if (Array.isArray(instructionAst) && instructionAst.length > 0) {
|
||||
const extractedRelationships: RelationshipInsertType[] = astToRelationship((instructionAst[0] as any), tables);
|
||||
relationships = relationships.concat(extractedRelationships);
|
||||
}
|
||||
} catch (error) {
|
||||
if ((error as any).relationships && (error as any).relationships.length > 0)
|
||||
relationships = relationships.concat((error as any).relationships);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return { tables, relationships, indices };
|
||||
|
||||
}
|
||||
@@ -120,8 +191,169 @@ export const SqlToDatabase = (sql: string, data_types: DataType[], dialect: Data
|
||||
|
||||
|
||||
|
||||
export const astToRelationship = (tables: TableInsertType[], targetTable?: TableInsertType, constrainttAst?: any, columnAst?: any, alterTableAst?: any): RelationshipInsertType | RelationshipInsertType[] => {
|
||||
|
||||
let targetField: FieldInsertType | undefined;
|
||||
let sourceTable: TableInsertType | undefined;
|
||||
let sourceField: FieldInsertType | undefined;
|
||||
|
||||
let relationships: RelationshipInsertType[] = [];
|
||||
|
||||
if (constrainttAst) {
|
||||
|
||||
targetField = targetTable?.fields?.find((field: FieldInsertType) => field.name == constrainttAst.definition?.[0].column);
|
||||
sourceTable = tables.find((table: TableInsertType) => table.name == constrainttAst.reference_definition?.table?.[0].table);
|
||||
sourceField = sourceTable?.fields?.find((field: FieldInsertType) => field.name == constrainttAst.reference_definition?.definition?.[0].column);
|
||||
|
||||
}
|
||||
|
||||
if (columnAst) {
|
||||
targetField = targetTable?.fields?.find((field: FieldInsertType) => field.name == columnAst.column?.column);
|
||||
sourceTable = tables.find((table: TableInsertType) => table.name == columnAst.reference_definition?.table?.[0].table);
|
||||
sourceField = sourceTable?.fields?.find((field: FieldInsertType) => field.name == columnAst.reference_definition?.definition?.[0].column);
|
||||
}
|
||||
|
||||
if (alterTableAst) {
|
||||
const expressions = alterTableAst.expr;
|
||||
const foreignKeyExpressions = expressions.filter((expression: any) => expression.resource == "constraint" && expression.create_definitions?.constraint_type == "FOREIGN KEY")
|
||||
|
||||
for (const expression of foreignKeyExpressions) {
|
||||
|
||||
const targetField: FieldInsertType | undefined = targetTable?.fields?.find((field: FieldInsertType) => field.name == expression.create_definitions?.definition?.[0].column);
|
||||
const sourceTable: TableInsertType | undefined = tables.find((table: TableInsertType) => table.name == expression.create_definitions?.reference_definition?.table?.[0].table)
|
||||
const sourceField: FieldInsertType | undefined = sourceTable?.fields?.find((field: FieldInsertType) => field.name == expression.create_definitions?.reference_definition?.definition?.[0].column);
|
||||
|
||||
|
||||
if (!targetField || !sourceField || !sourceTable)
|
||||
continue;
|
||||
|
||||
relationships.push({
|
||||
id: v4(),
|
||||
targetTableId: targetTable?.id,
|
||||
targetFieldId: targetField.id,
|
||||
sourceTableId: sourceTable.id,
|
||||
sourceFieldId: sourceField.id,
|
||||
cardinality: targetField.unique ? Cardinality.one_to_one : Cardinality.one_to_many
|
||||
} as RelationshipInsertType)
|
||||
}
|
||||
|
||||
if (relationships.length == foreignKeyExpressions.length)
|
||||
return relationships;
|
||||
else
|
||||
throw Error({
|
||||
success: false,
|
||||
message: "Failed to Extract all relationships",
|
||||
relationships
|
||||
} as any)
|
||||
}
|
||||
|
||||
if (!targetField || !sourceField || !sourceTable)
|
||||
throw Error("Failed to extract relationship");
|
||||
|
||||
|
||||
return {
|
||||
id: v4(),
|
||||
targetTableId: targetTable?.id,
|
||||
targetFieldId: targetField.id,
|
||||
sourceTableId: sourceTable.id,
|
||||
sourceFieldId: sourceField.id,
|
||||
cardinality: targetField.unique ? Cardinality.one_to_one : Cardinality.one_to_many
|
||||
} as RelationshipInsertType;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const astToTable = (ast: any, data_types: DataType[]): TableInsertType => {
|
||||
console.log(ast)
|
||||
return {
|
||||
id: v4(),
|
||||
name: ast.table[0]?.table,
|
||||
fields: ast.create_definitions.filter((column: any) => column.resource == "column")
|
||||
.map((fieldAst: any, index: number) => astToField(fieldAst, data_types, index)),
|
||||
color: randomColor()
|
||||
} as TableInsertType;
|
||||
}
|
||||
|
||||
|
||||
export const astToField = (ast: any, data_types: DataType[], sequence: number): FieldInsertType => {
|
||||
|
||||
|
||||
|
||||
const dataType: DataType | undefined = data_types.find((dataType: DataType) => {
|
||||
const synonyms: string[] = dataType.synonyms ? JSON.parse(dataType.synonyms) : [];
|
||||
return dataType.name == ast.definition.dataType?.toLowerCase() || synonyms.includes(ast.definition.dataType?.toLowerCase())
|
||||
});
|
||||
|
||||
let values: string | undefined = undefined;
|
||||
|
||||
const modifiers: string[] = dataType?.modifiers ? JSON.parse(dataType.modifiers) : [];
|
||||
|
||||
const { length, scale } = ast.definition;
|
||||
const { character_set, collate: collation } = ast;
|
||||
|
||||
let charset: string | undefined;
|
||||
let collate: string | undefined;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
let defaultValue: string | undefined = ast.default_val?.value?.value ? ast.default_val?.value?.value : undefined;
|
||||
|
||||
let maxLength: number | null = null;
|
||||
let precision: number | null = null;
|
||||
|
||||
if (modifiers.includes(Modifiers.LENGTH) && length)
|
||||
maxLength = length;
|
||||
|
||||
if (modifiers.includes(Modifiers.PRECISION) && length)
|
||||
precision = length;
|
||||
|
||||
|
||||
if (modifiers.includes(Modifiers.CHARSET) && character_set)
|
||||
charset = character_set.value?.value;
|
||||
|
||||
if (modifiers.includes(Modifiers.COLLATE) && collation)
|
||||
collate = collation.collate?.name;
|
||||
|
||||
if (modifiers.includes(Modifiers.VALUES)) {
|
||||
|
||||
|
||||
const value = ast.definition?.expr?.value.map((value: any) => value.value);
|
||||
if (value)
|
||||
values = JSON.stringify(value);
|
||||
}
|
||||
|
||||
if (dataType?.type == DataTypes.TIME &&
|
||||
ast.default_val?.value?.type == "function" &&
|
||||
ast.default_val?.value?.name?.name?.length > 0 &&
|
||||
ast.default_val?.value?.name?.name[0].value == "CURRENT_TIMESTAMP")
|
||||
defaultValue = TimeDefaultValues.NOW;
|
||||
|
||||
return {
|
||||
id: v4(),
|
||||
name: ast.column.column,
|
||||
defaultValue,
|
||||
typeId: dataType?.id,
|
||||
nullable: ast.nullable?.value != "not null",
|
||||
unique: ast.unique == "unique",
|
||||
maxLength,
|
||||
precision,
|
||||
scale,
|
||||
sequence,
|
||||
autoIncrement: ast.auto_increment == "auto_increment",
|
||||
isPrimary: ast.primary_key == "primary key",
|
||||
values,
|
||||
charset,
|
||||
collate,
|
||||
} as FieldInsertType;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
export const postgresAstToTable = (ast: any, data_types: DataType[], postgresTypes: any[]): TableInsertType => {
|
||||
// console.log(ast);
|
||||
|
||||
return {
|
||||
id: v4(),
|
||||
@@ -133,6 +365,7 @@ export const postgresAstToTable = (ast: any, data_types: DataType[], postgresTyp
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const postgresAstToField = (ast: any, data_types: DataType[], sequence: number, postgresTypes: any[]): FieldInsertType => {
|
||||
|
||||
let dataType: DataType | undefined = data_types.find((dataType: DataType) => {
|
||||
@@ -152,12 +385,12 @@ export const postgresAstToField = (ast: any, data_types: DataType[], sequence: n
|
||||
}
|
||||
|
||||
if (ast.dataType.name?.toLowerCase().includes("serial")) {
|
||||
|
||||
|
||||
let baseType: string = ast.dataType.name?.toLowerCase() == "serial" ? "integer" : ast.dataType.name?.toLowerCase().replace("serial", "int");
|
||||
dataType = data_types.find((dataType: DataType) => {
|
||||
return dataType.name == baseType;
|
||||
});
|
||||
autoIncrement = true;
|
||||
autoIncrement = true;
|
||||
}
|
||||
const modifiers: string[] = dataType?.modifiers ? JSON.parse(dataType.modifiers) : [];
|
||||
|
||||
@@ -165,6 +398,7 @@ export const postgresAstToField = (ast: any, data_types: DataType[], sequence: n
|
||||
let scale: number | undefined;
|
||||
let unique: boolean = false;
|
||||
let primaryKey: boolean = false;
|
||||
|
||||
if (ast.dataType.config && ast.dataType.config.length > 0) {
|
||||
if (ast.dataType.config.length >= 1)
|
||||
length = ast.dataType.config[0];
|
||||
@@ -230,52 +464,8 @@ export const postgresAstToField = (ast: any, data_types: DataType[], sequence: n
|
||||
}
|
||||
|
||||
|
||||
const astToTable = (ast: any, data_types: DataType[]): TableInsertType => {
|
||||
|
||||
|
||||
return {
|
||||
id: v4(),
|
||||
name: ast.table[0]?.table,
|
||||
fields: ast.create_definitions.filter((column: any) => column.resource == "column")
|
||||
.map((fieldAst: any) => astToField(fieldAst, data_types))
|
||||
} as TableInsertType;
|
||||
}
|
||||
|
||||
|
||||
export const astToField = (ast: any, data_types: DataType[]): FieldInsertType => {
|
||||
|
||||
const dataType: DataType | undefined = data_types.find((dataType: DataType) => dataType.name == ast.definition.dataType?.toLowerCase());
|
||||
const modifiers: string[] = dataType?.modifiers ? JSON.parse(dataType.modifiers) : [];
|
||||
|
||||
const { length, scale } = ast.definition;
|
||||
|
||||
let maxLength: number | null = null;
|
||||
let precision: number | null = null;
|
||||
|
||||
if (modifiers.includes(Modifiers.LENGTH) && length)
|
||||
maxLength = length;
|
||||
|
||||
if (modifiers.includes(Modifiers.PRECISION) && length)
|
||||
precision = length;
|
||||
|
||||
|
||||
return {
|
||||
id: v4(),
|
||||
name: ast.column.column,
|
||||
defaultValue: ast.default_val,
|
||||
typeId: dataType?.id,
|
||||
nullable: ast.nullable?.value == "not null",
|
||||
unique: ast.unique,
|
||||
maxLength,
|
||||
precision,
|
||||
scale: scale,
|
||||
|
||||
} as FieldInsertType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const astToRelationship = (ast: any, tables: TableInsertType[]): RelationshipInsertType[] => {
|
||||
export const postgresAstToRelationship = (ast: any, tables: TableInsertType[]): RelationshipInsertType[] => {
|
||||
|
||||
|
||||
const relationships: RelationshipInsertType[] = [];
|
||||
@@ -357,6 +547,31 @@ const foreignKeyConstraintToAlterTableAst = (constraints: any[], table: TableIns
|
||||
}
|
||||
|
||||
export const astToIndex = (ast: any, tables: TableInsertType[]): IndexInsertType => {
|
||||
const table: TableInsertType | undefined = tables.find((table: TableInsertType) => table.name == ast.table.table);
|
||||
|
||||
if (!table)
|
||||
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,
|
||||
tableId: table.id,
|
||||
unique: ast.index_type == "unique",
|
||||
fieldIndices: fieldIds?.map((id: string) => ({
|
||||
id: v4(),
|
||||
fieldId: id
|
||||
}))
|
||||
} as IndexInsertType
|
||||
}
|
||||
|
||||
export const postgresAstToIndex = (ast: any, tables: TableInsertType[]): IndexInsertType => {
|
||||
const table: TableInsertType | undefined = tables.find((table: TableInsertType) => table.name == ast.table.name);
|
||||
|
||||
if (!table)
|
||||
|
||||
Reference in New Issue
Block a user