diff --git a/frontend/packages/db-structure/src/parser/errors.ts b/frontend/packages/db-structure/src/parser/errors.ts index 6ee70ffda..857ac521c 100644 --- a/frontend/packages/db-structure/src/parser/errors.ts +++ b/frontend/packages/db-structure/src/parser/errors.ts @@ -11,3 +11,10 @@ export class WarningError extends ProcessError { this.name = 'WarningError' } } + +export class UnexpectedTokenWarningError extends WarningError { + constructor(message: string) { + super(message) + this.name = 'UnexpectedTokenWarningError' + } +} diff --git a/frontend/packages/db-structure/src/parser/sql/postgresql/converter.ts b/frontend/packages/db-structure/src/parser/sql/postgresql/converter.ts index 3db5961aa..6a69106e1 100644 --- a/frontend/packages/db-structure/src/parser/sql/postgresql/converter.ts +++ b/frontend/packages/db-structure/src/parser/sql/postgresql/converter.ts @@ -7,6 +7,7 @@ import type { List, Node, String as PgString, + RawStmt, } from '@pgsql/types' import type { Columns, @@ -19,7 +20,6 @@ import { defaultRelationshipName, handleOneToOneRelationships, } from '../../utils/index.js' -import type { RawStmtWrapper } from './parser.js' function isStringNode(node: Node | undefined): node is { String: PgString } { return ( @@ -94,7 +94,7 @@ const constraintToRelationship = ( } // Transform function for AST to DBStructure -export const convertToDBStructure = (ast: RawStmtWrapper[]): DBStructure => { +export const convertToDBStructure = (stmts: RawStmt[]): DBStructure => { const tables: Record = {} const relationships: Record = {} @@ -339,16 +339,7 @@ export const convertToDBStructure = (ast: RawStmtWrapper[]): DBStructure => { } } - if (!ast) { - return { - tables: {}, - relationships: {}, - } - } - - // pg-query-emscripten does not have types, so we need to define them ourselves - // @ts-expect-error - for (const statement of ast.parse_tree.stmts) { + for (const statement of stmts) { if (statement?.stmt === undefined) continue const stmt = statement.stmt diff --git a/frontend/packages/db-structure/src/parser/sql/postgresql/index.test.ts b/frontend/packages/db-structure/src/parser/sql/postgresql/index.test.ts index d73e8f76c..4e06836b5 100644 --- a/frontend/packages/db-structure/src/parser/sql/postgresql/index.test.ts +++ b/frontend/packages/db-structure/src/parser/sql/postgresql/index.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from 'vitest' import { parserTestCases } from '../../__tests__/index.js' +import { UnexpectedTokenWarningError } from '../../errors.js' import { processor } from './index.js' describe(processor, () => { @@ -209,4 +210,19 @@ describe(processor, () => { ) }) }) + + describe('abnormal cases', () => { + it('show error if the syntax is broken', async () => { + const result = await processor(/* sql */ ` + CREATEe TABLE posts (); + `) + + const value = { tables: {}, relationships: {} } + const errors = [ + new UnexpectedTokenWarningError('syntax error at or near "CREATEe"'), + ] + + expect(result).toEqual({ value, errors }) + }) + }) }) diff --git a/frontend/packages/db-structure/src/parser/sql/postgresql/index.ts b/frontend/packages/db-structure/src/parser/sql/postgresql/index.ts index f6d68d11b..7f7bdfa38 100644 --- a/frontend/packages/db-structure/src/parser/sql/postgresql/index.ts +++ b/frontend/packages/db-structure/src/parser/sql/postgresql/index.ts @@ -1,4 +1,5 @@ import type { DBStructure } from '../../../schema/index.js' +import { UnexpectedTokenWarningError } from '../../errors.js' import type { Processor } from '../../types.js' import { convertToDBStructure } from './converter.js' import { mergeDBStructures } from './mergeDBStructures.js' @@ -8,12 +9,17 @@ import { processSQLInChunks } from './processSQLInChunks.js' export const processor: Processor = async (str: string) => { const dbStructure: DBStructure = { tables: {}, relationships: {} } const CHUNK_SIZE = 1000 + const errors: Error[] = [] await processSQLInChunks(str, CHUNK_SIZE, async (chunk) => { - const parsed = await parse(chunk) - const partial = convertToDBStructure(parsed) + const { parse_tree, error } = await parse(chunk) + const partial = convertToDBStructure(parse_tree.stmts) mergeDBStructures(dbStructure, partial) + + if (error !== null) { + errors.push(new UnexpectedTokenWarningError(error.message)) + } }) - return { value: dbStructure, errors: [] } + return { value: dbStructure, errors } } diff --git a/frontend/packages/db-structure/src/parser/sql/postgresql/parser.ts b/frontend/packages/db-structure/src/parser/sql/postgresql/parser.ts index 37bafd4a1..fbcdb66b3 100644 --- a/frontend/packages/db-structure/src/parser/sql/postgresql/parser.ts +++ b/frontend/packages/db-structure/src/parser/sql/postgresql/parser.ts @@ -3,14 +3,25 @@ import type { RawStmt } from '@pgsql/types' // @ts-expect-error import Module from 'pg-query-emscripten' -export const parse = async (str: string): Promise => { +export const parse = async (str: string): Promise => { const pgQuery = await new Module() const result = pgQuery.parse(str) return result } -// It was expected that postgresParse would return a ParseResult object, -// but it was found that an array of RawStmtWrapper objects was returned. -export interface RawStmtWrapper { - RawStmt: RawStmt +// NOTE: pg-query-emscripten does not have types, so we need to define them ourselves +export interface ParseResult { + parse_tree: { + version: number + stmts: RawStmt[] + } + stderr_buffer: string + error: { + message: string + funcname: string + filename: string + lineno: number + cursorpos: number + context: string + } | null }