Skip to content

Commit

Permalink
Merge pull request #231 from liam-hq/show-error-when-parse-postgres
Browse files Browse the repository at this point in the history
feat: show error when parse postgres schema
  • Loading branch information
MH4GF authored Dec 12, 2024
2 parents 77d3382 + 7c67d95 commit 58f22d2
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 20 deletions.
7 changes: 7 additions & 0 deletions frontend/packages/db-structure/src/parser/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
List,
Node,
String as PgString,
RawStmt,
} from '@pgsql/types'
import type {
Columns,
Expand All @@ -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 (
Expand Down Expand Up @@ -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<string, Table> = {}
const relationships: Record<string, Relationship> = {}

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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, () => {
Expand Down Expand Up @@ -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 })
})
})
})
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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 }
}
21 changes: 16 additions & 5 deletions frontend/packages/db-structure/src/parser/sql/postgresql/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<RawStmtWrapper[]> => {
export const parse = async (str: string): Promise<ParseResult> => {
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
}

0 comments on commit 58f22d2

Please sign in to comment.