Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

108 changes: 108 additions & 0 deletions frontend/packages/schema/src/parser/drizzle/mysql/astUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
Import,
ObjectExpression,
Super,
TemplateLiteral,
} from '@swc/core'
import { getPropertyValue, hasProperty, isObject } from './types.js'

Expand Down Expand Up @@ -153,6 +154,113 @@ export const getIdentifierName = (node: Expression): string | null => {
return null
}

/**
* Check if a call expression calls a function with a specific name
*/
export const isCallToFunction = (
callExpr: CallExpression,
functionName: string,
): boolean => {
return (
isIdentifier(callExpr.callee) &&
callExpr.callee.value === functionName &&
callExpr.arguments.length > 0
Comment on lines +166 to +167
)
}

/**
* Extract column names from an array expression of member expressions
* Handles patterns like: [table.column1, table.column2]
*/
export const extractColumnNames = (expr: Expression): string[] => {
if (!isArrayExpression(expr)) return []

const columns: string[] = []

for (const elem of expr.elements) {
if (!elem) continue
const elemExpr = getArgumentExpression(elem)
if (!elemExpr) continue
if (!isMemberExpression(elemExpr)) continue
if (!isIdentifier(elemExpr.property)) continue
columns.push(elemExpr.property.value)
}

return columns
}

/**
* Extract table and column names from member expressions in an array
* Returns the first table name found and all column names
*/
export const extractTableAndColumns = (
expr: Expression,
): { tableName: string | null; columnNames: string[] } => {
if (!isArrayExpression(expr)) {
return { tableName: null, columnNames: [] }
}

const columnNames: string[] = []
let tableName: string | null = null

for (const elem of expr.elements) {
if (!elem) continue
const elemExpr = getArgumentExpression(elem)

if (
elemExpr &&
isMemberExpression(elemExpr) &&
isIdentifier(elemExpr.object) &&
isIdentifier(elemExpr.property)
) {
tableName = tableName ?? elemExpr.object.value
columnNames.push(elemExpr.property.value)
}
}

return { tableName, columnNames }
}

/**
* Reconstruct a sql template literal condition string from quasis and expressions.
* For MemberExpression like table.age, extracts the JS property name (e.g., "age").
* Falls back to "?" for other expression types.
* e.g. sql`${table.age} >= 0` → "age >= 0"
*/
export const reconstructSqlTemplate = (template: TemplateLiteral): string => {
const parts: string[] = []
for (let i = 0; i < template.quasis.length; i++) {
const quasi = template.quasis[i]
if (quasi) parts.push(quasi.raw)
if (i < template.expressions.length) {
const expr = template.expressions[i]
if (
expr &&
expr.type === 'MemberExpression' &&
expr.property.type === 'Identifier'
) {
parts.push(expr.property.value)
} else {
parts.push('?')
}
}
}
return parts.join('')
}

/**
* Extract configuration object from a call expression's first argument
*/
export const extractConfigObject = (
callExpr: CallExpression,
): ObjectExpression | null => {
if (callExpr.arguments.length === 0) return null

const configArg = callExpr.arguments[0]
const configExpr = getArgumentExpression(configArg)
return isObjectExpression(configExpr) ? configExpr : null
}

/**
* Parse method call chain from a call expression
*/
Expand Down
32 changes: 31 additions & 1 deletion frontend/packages/schema/src/parser/drizzle/mysql/converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ const convertToTable = (
.filter((name) => name && name.length > 0)

// Create composite primary key constraint
const constraintName = `${tableDef.name}_pkey`
const constraintName =
tableDef.compositePrimaryKey.name ?? `${tableDef.name}_pkey`
constraints[constraintName] = {
type: 'PRIMARY KEY',
name: constraintName,
Expand All @@ -153,6 +154,35 @@ const convertToTable = (
}
}

// Handle table-level foreign key definitions
if (tableDef.foreignKeys) {
for (const fkDef of tableDef.foreignKeys) {
const targetTableName =
variableToTableMapping[fkDef.targetTable] ?? fkDef.targetTable
const columnNames = fkDef.columns.map(
(jsName) => tableDef.columns[jsName]?.name ?? jsName,
)
const constraintName =
fkDef.name ??
`${tableDef.name}_${columnNames.join('_')}_${fkDef.targetTable}_${fkDef.targetColumns.join('_')}_fk`

const constraint: ForeignKeyConstraint = {
type: 'FOREIGN KEY',
name: constraintName,
columnNames,
targetTableName,
targetColumnNames: fkDef.targetColumns,
updateConstraint: fkDef.onUpdate
? convertReferenceOption(fkDef.onUpdate)
: 'NO_ACTION',
deleteConstraint: fkDef.onDelete
? convertReferenceOption(fkDef.onDelete)
: 'NO_ACTION',
}
constraints[constraintName] = constraint
}
}

// Convert indexes
for (const [_, indexDef] of Object.entries(tableDef.indexes)) {
// Map JS property names to actual column names
Expand Down
Loading