mirror of
https://github.com/stackrender/stackrender.git
synced 2026-09-10 19:25:44 +00:00
193 lines
5.6 KiB
TypeScript
193 lines
5.6 KiB
TypeScript
import { DatabaseDialect } from "@/lib/database";
|
|
import { DataType } from "@/lib/schemas/data-type-schema";
|
|
import MysqlRenderer from "./database/mysql-renderer";
|
|
import PostgresqlRenderer from "./database/postgresql-renderer";
|
|
import MariaDbRenderer from "./database/mariadb-renderer";
|
|
import OracleRenderer from "./database/oracle-renderer";
|
|
import MSSqlRenderer from "./database/mssql-database-renderer";
|
|
import SqliteRenderer from "./database/sqlite-database-renderer";
|
|
import { TableType } from "@/lib/schemas/table-schema";
|
|
import { FieldType } from "@/lib/schemas/field-schema";
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
export const getPostgresEnumName = (table: TableType, field: FieldType): string => {
|
|
|
|
return `${table.name}_${field.name.toLowerCase()}_enum`
|
|
}
|
|
|
|
export const getRenderer = (dialect: DatabaseDialect, data_types: DataType[]) => {
|
|
|
|
switch (dialect) {
|
|
case DatabaseDialect.MYSQL:
|
|
return new MysqlRenderer(data_types);
|
|
|
|
case DatabaseDialect.POSTGRES:
|
|
return new PostgresqlRenderer(data_types);
|
|
|
|
case DatabaseDialect.MARIADB:
|
|
return new MariaDbRenderer(data_types);
|
|
|
|
case DatabaseDialect.ORACLE:
|
|
return new OracleRenderer(data_types);
|
|
|
|
case DatabaseDialect.MSSQL:
|
|
return new MSSqlRenderer(data_types);
|
|
|
|
case DatabaseDialect.SQLITE:
|
|
return new SqliteRenderer(data_types);
|
|
}
|
|
} |