diff --git a/src/lib/connectors/index.ts b/src/lib/connectors/index.ts index 8cb9307..3887434 100644 --- a/src/lib/connectors/index.ts +++ b/src/lib/connectors/index.ts @@ -16,6 +16,11 @@ export interface Connector { tableName: string, structureFetched: (tableName: string, structure: string) => void ) => Promise; + getTableStructureBatch: ( + databaseName: string, + tableNameList: string[], + structureFetched: (tableName: string, structure: string) => void + ) => Promise; } export const newConnector = (connection: Connection): Connector => { diff --git a/src/lib/connectors/mssql/index.ts b/src/lib/connectors/mssql/index.ts index b7f0138..877014d 100644 --- a/src/lib/connectors/mssql/index.ts +++ b/src/lib/connectors/mssql/index.ts @@ -117,6 +117,39 @@ const getTableStructure = async ( ); }; +const getTableStructureBatch = async ( + connection: Connection, + databaseName: string, + tableNameList: string[], + structureFetched: (tableName: string, structure: string) => void +): Promise => { + const pool = await getMSSQLConnection(connection); + const request = pool.request(); + + await Promise.all( + tableNameList.map(async (tableName) => { + const { recordset } = await request.query( + `SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE FROM ${databaseName}.INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='dbo' AND TABLE_NAME='${tableName}';` + ); + const columnList = []; + // Transform to standard schema string. + for (const row of recordset) { + columnList.push( + `${row["COLUMN_NAME"]} ${row["DATA_TYPE"].toUpperCase()} ${ + String(row["IS_NULLABLE"]).toUpperCase() === "NO" ? "NOT NULL" : "" + }` + ); + } + structureFetched( + tableName, + `CREATE TABLE [${tableName}] ( + ${columnList.join(",\n")} + );` + ); + }) + ); +}; + const newConnector = (connection: Connection): Connector => { return { testConnection: () => testConnection(connection), @@ -130,6 +163,17 @@ const newConnector = (connection: Connection): Connector => { structureFetched: (tableName: string, structure: string) => void ) => getTableStructure(connection, databaseName, tableName, structureFetched), + getTableStructureBatch: ( + databaseName: string, + tableNameList: string[], + structureFetched: (tableName: string, structure: string) => void + ) => + getTableStructureBatch( + connection, + databaseName, + tableNameList, + structureFetched + ), }; }; diff --git a/src/lib/connectors/mysql/index.ts b/src/lib/connectors/mysql/index.ts index 2abb90b..81cf9a3 100644 --- a/src/lib/connectors/mysql/index.ts +++ b/src/lib/connectors/mysql/index.ts @@ -111,6 +111,29 @@ const getTableStructure = async ( structureFetched(tableName, rows[0]["Create Table"] || ""); }; +const getTableStructureBatch = async ( + connection: Connection, + databaseName: string, + tableNameList: string[], + structureFetched: (tableName: string, structure: string) => void +): Promise => { + const conn = await getMySQLConnection(connection); + + await Promise.all( + tableNameList.map(async (tableName) => { + const [rows] = await conn.query( + `SHOW CREATE TABLE \`${databaseName}\`.\`${tableName}\`;` + ); + if (rows.length !== 1) { + throw new Error("Unexpected number of rows."); + } + structureFetched(tableName, rows[0]["Create Table"] || ""); + }) + ).finally(() => { + conn.destroy(); + }); +}; + const newConnector = (connection: Connection): Connector => { return { testConnection: () => testConnection(connection), @@ -124,6 +147,17 @@ const newConnector = (connection: Connection): Connector => { structureFetched: (tableName: string, structure: string) => void ) => getTableStructure(connection, databaseName, tableName, structureFetched), + getTableStructureBatch: ( + databaseName: string, + tableNameList: string[], + structureFetched: (tableName: string, structure: string) => void + ) => + getTableStructureBatch( + connection, + databaseName, + tableNameList, + structureFetched + ), }; }; diff --git a/src/lib/connectors/postgres/index.ts b/src/lib/connectors/postgres/index.ts index 8eec650..31ab8f4 100644 --- a/src/lib/connectors/postgres/index.ts +++ b/src/lib/connectors/postgres/index.ts @@ -121,6 +121,42 @@ const getTableStructure = async ( ); }; +const getTableStructureBatch = async ( + connection: Connection, + databaseName: string, + tableNameList: string[], + structureFetched: (tableName: string, structure: string) => void +): Promise => { + connection.database = databaseName; + const client = newPostgresClient(connection); + await client.connect(); + await Promise.all( + tableNameList.map(async (tableName) => { + const { rows } = await client.query( + `SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_schema='public' AND table_name=$1;`, + [tableName] + ); + const columnList = []; + // TODO(steven): transform it to standard schema string. + for (const row of rows) { + columnList.push( + `${row["column_name"]} ${row["data_type"].toUpperCase()} ${ + String(row["is_nullable"]).toUpperCase() === "NO" ? "NOT NULL" : "" + }` + ); + } + structureFetched( + tableName, + `CREATE TABLE \`${tableName}\` ( + ${columnList.join(",\n")} + );` + ); + }) + ).finally(async () => { + await client.end(); + }); +}; + const newConnector = (connection: Connection): Connector => { return { testConnection: () => testConnection(connection), @@ -134,6 +170,17 @@ const newConnector = (connection: Connection): Connector => { structureFetched: (tableName: string, structure: string) => void ) => getTableStructure(connection, databaseName, tableName, structureFetched), + getTableStructureBatch: ( + databaseName: string, + tableNameList: string[], + structureFetched: (tableName: string, structure: string) => void + ) => + getTableStructureBatch( + connection, + databaseName, + tableNameList, + structureFetched + ), }; }; diff --git a/src/pages/api/connection/db_schema.ts b/src/pages/api/connection/db_schema.ts index cc8280a..1c2388d 100644 --- a/src/pages/api/connection/db_schema.ts +++ b/src/pages/api/connection/db_schema.ts @@ -22,14 +22,14 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { structure, }); }; - Promise.all( - rawTableNameList.map(async (tableName) => - connector.getTableStructure(db, tableName, structureFetched) - ) - ).then(() => { - res.status(200).json({ - data: tableStructures, - }); + await connector.getTableStructureBatch( + db, + rawTableNameList, + structureFetched + ); + + res.status(200).json({ + data: tableStructures, }); } catch (error: any) { res.status(400).json({