From c551919f92a707fae8f7e0d8c1bf76c522c757b3 Mon Sep 17 00:00:00 2001 From: FunamaYukina Date: Thu, 28 Nov 2024 15:18:31 +0900 Subject: [PATCH 1/5] [add] postgresql parse test --- .../src/parser/sql/postgresql/index.test.ts | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 frontend/packages/db-structure/src/parser/sql/postgresql/index.test.ts 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 new file mode 100644 index 000000000..94b93222f --- /dev/null +++ b/frontend/packages/db-structure/src/parser/sql/postgresql/index.test.ts @@ -0,0 +1,70 @@ +import type { Table } from 'src/schema' +import { aColumn, aDBStructure, aTable } from 'src/schema/factories' +import { describe, expect, it } from 'vitest' +import { processor } from '.' + +describe(processor, () => { + describe('should parse create_table correctry', () => { + const userTable = (override?: Partial) => + aDBStructure({ + tables: { + users: aTable({ + name: 'users', + columns: { + id: aColumn({ + name: 'id', + type: 'serial', + notNull: true, + primary: true, + }), + ...override?.columns, + }, + }), + }, + }) + + it('not null', () => { + const result = processor(/* PostgreSQL */ ` + CREATE TABLE users ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL + ); + `) + + const expected = userTable({ + columns: { + name: aColumn({ + name: 'name', + type: 'varchar', + notNull: true, + }), + }, + }) + + expect(result).toEqual(expected) + }) + + it('nullable', () => { + const result = processor(/* PostgreSQL */ ` + CREATE TABLE users ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) + ); + `) + + const expected = userTable({ + columns: { + name: aColumn({ + name: 'name', + type: 'varchar', + notNull: false, + }), + }, + }) + + expect(result).toEqual(expected) + }) + + // TODO: Implement default value + }) +}) From b9e95edcefc6891bd4c63b7700feddbcdcb030b1 Mon Sep 17 00:00:00 2001 From: FunamaYukina Date: Thu, 28 Nov 2024 15:25:23 +0900 Subject: [PATCH 2/5] [fix] default values and the type retrieval part --- .../parser/__snapshots__/index.test.ts.snap | 47 ++++++++++++++++++- .../src/parser/sql/postgresql/converter.ts | 42 +++++++++-------- 2 files changed, 68 insertions(+), 21 deletions(-) diff --git a/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap b/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap index 4d8c0df4d..e5afb5edd 100644 --- a/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap +++ b/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap @@ -5,7 +5,52 @@ exports[`parse > should parse postgresql to JSON correctly 1`] = ` "relationships": {}, "tables": { "users": { - "columns": {}, + "columns": { + "created_at": { + "check": null, + "comment": null, + "default": null, + "increment": false, + "name": "created_at", + "notNull": false, + "primary": false, + "type": "timestamp", + "unique": false, + }, + "email": { + "check": null, + "comment": null, + "default": null, + "increment": false, + "name": "email", + "notNull": true, + "primary": false, + "type": "varchar", + "unique": true, + }, + "id": { + "check": null, + "comment": null, + "default": null, + "increment": false, + "name": "id", + "notNull": true, + "primary": true, + "type": "serial", + "unique": false, + }, + "username": { + "check": null, + "comment": null, + "default": null, + "increment": false, + "name": "username", + "notNull": true, + "primary": false, + "type": "varchar", + "unique": true, + }, + }, "comment": null, "indices": [], "name": "users", 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 addf7b174..62b3de707 100644 --- a/frontend/packages/db-structure/src/parser/sql/postgresql/converter.ts +++ b/frontend/packages/db-structure/src/parser/sql/postgresql/converter.ts @@ -1,10 +1,4 @@ -import type { - ColumnDef, - Constraint, - CreateStmt, - Node, - String as PgString, -} from '@pgsql/types' +import type { ColumnDef, Constraint, CreateStmt, Node } from '@pgsql/types' import type { Columns, DBStructure, Table } from 'src/schema' import type { RawStmtWrapper } from './parser' @@ -12,8 +6,18 @@ import type { RawStmtWrapper } from './parser' export const convertToDBStructure = (ast: RawStmtWrapper[]): DBStructure => { const tables: Record = {} + interface PgString { + sval: string + str: string + } + function isStringNode(node: Node): node is { String: PgString } { - return (node as { String: { str: string } }).String !== undefined + return ( + (node as { String: { sval: string; str: string } }).String !== + undefined && + (node as { String: { sval: string; str: string } }).String.str !== + 'pg_catalog' + ) } function isConstraintNode(node: Node): node is { Constraint: Constraint } { @@ -40,21 +44,18 @@ export const convertToDBStructure = (ast: RawStmtWrapper[]): DBStructure => { const tableName = createStmt.relation.relname const columns: Columns = {} - createStmt.tableElts - .filter( - (elt: Node): elt is { ColumnDef: ColumnDef } => 'ColumnDef' in elt, - ) - .map((elt) => { + for (const elt of createStmt.tableElts) { + if ('ColumnDef' in elt) { const colDef = elt.ColumnDef - return { + columns[colDef.colname || ''] = { name: colDef.colname || '', type: colDef.typeName?.names ?.filter(isStringNode) - .map((n) => n.String.sval) - .join(' ') || '', - default: '', // TODO - check: '', // TODO + .map((n) => n.String.str) + .join('') || '', + default: null, // TODO + check: null, // TODO primary: colDef.constraints ?.filter(isConstraintNode) @@ -77,9 +78,10 @@ export const convertToDBStructure = (ast: RawStmtWrapper[]): DBStructure => { colDef.typeName?.names ?.filter(isStringNode) .some((n) => n.String.sval === 'serial') || false, - comment: '', // TODO + comment: null, // TODO } - }) + } + } if (tableName) { tables[tableName] = { From 6f1bccd272b5d538cd8ba3b1d55c485a0e728d20 Mon Sep 17 00:00:00 2001 From: FunamaYukina Date: Thu, 28 Nov 2024 15:42:22 +0900 Subject: [PATCH 3/5] [Refactor]Remove unused import --- .../db-structure/src/parser/sql/postgresql/converter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 62b3de707..bdae5a16e 100644 --- a/frontend/packages/db-structure/src/parser/sql/postgresql/converter.ts +++ b/frontend/packages/db-structure/src/parser/sql/postgresql/converter.ts @@ -1,4 +1,4 @@ -import type { ColumnDef, Constraint, CreateStmt, Node } from '@pgsql/types' +import type { Constraint, CreateStmt, Node } from '@pgsql/types' import type { Columns, DBStructure, Table } from 'src/schema' import type { RawStmtWrapper } from './parser' From 9e4c21b07a4a1f1acf1e5e2a022d07da9eed2e05 Mon Sep 17 00:00:00 2001 From: FunamaYukina Date: Thu, 28 Nov 2024 16:03:01 +0900 Subject: [PATCH 4/5] [add] code comment --- .../db-structure/src/parser/sql/postgresql/converter.ts | 2 ++ 1 file changed, 2 insertions(+) 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 bdae5a16e..317eb5640 100644 --- a/frontend/packages/db-structure/src/parser/sql/postgresql/converter.ts +++ b/frontend/packages/db-structure/src/parser/sql/postgresql/converter.ts @@ -6,6 +6,8 @@ import type { RawStmtWrapper } from './parser' export const convertToDBStructure = (ast: RawStmtWrapper[]): DBStructure => { const tables: Record = {} + // Creating a new variable str to hold the returned string value, + // as it's not a property of the String type. interface PgString { sval: string str: string From 35d141458e74699908a3ec68eeaee2f11eb51b94 Mon Sep 17 00:00:00 2001 From: FunamaYukina Date: Thu, 28 Nov 2024 17:10:23 +0900 Subject: [PATCH 5/5] [refactor] isStringNode function Co-authored-by: Hirotaka Miyagi <31152321+MH4GF@users.noreply.github.com> --- .../db-structure/src/parser/sql/postgresql/converter.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 317eb5640..9920bf2f8 100644 --- a/frontend/packages/db-structure/src/parser/sql/postgresql/converter.ts +++ b/frontend/packages/db-structure/src/parser/sql/postgresql/converter.ts @@ -15,10 +15,11 @@ export const convertToDBStructure = (ast: RawStmtWrapper[]): DBStructure => { function isStringNode(node: Node): node is { String: PgString } { return ( - (node as { String: { sval: string; str: string } }).String !== - undefined && - (node as { String: { sval: string; str: string } }).String.str !== - 'pg_catalog' + 'String' in node && + typeof node.String === 'object' && + node.String !== null && + 'str' in node.String && + node.String.str !== 'pg_catalog' ) }