Attempting to fix mysql issue by changing our usage of mysql2 to use the promise version, making sure disconnection always occurs correctly and using a slightly different syntax/approach.

This commit is contained in:
mike12345567 2022-03-03 19:20:26 +00:00
parent 40740eaba0
commit ba3940f825
2 changed files with 92 additions and 85 deletions

View File

@ -16,7 +16,7 @@ import {
import { DatasourcePlus } from "./base/datasourcePlus" import { DatasourcePlus } from "./base/datasourcePlus"
module MySQLModule { module MySQLModule {
const mysql = require("mysql2") const mysql = require("mysql2/promise")
const Sql = require("./base/sql") const Sql = require("./base/sql")
interface MySQLConfig { interface MySQLConfig {
@ -29,7 +29,7 @@ module MySQLModule {
} }
const SCHEMA: Integration = { const SCHEMA: Integration = {
docs: "https://github.com/mysqljs/mysql", docs: "https://github.com/sidorares/node-mysql2",
plus: true, plus: true,
friendlyName: "MySQL", friendlyName: "MySQL",
description: description:
@ -80,36 +80,9 @@ module MySQLModule {
}, },
} }
function internalQuery(
client: any,
query: SqlQuery,
connect: boolean = true
): Promise<any[] | any> {
// Node MySQL is callback based, so we must wrap our call in a promise
return new Promise((resolve, reject) => {
if (connect) {
client.connect()
}
return client.query(
query.sql,
query.bindings || {},
(error: any, results: object[]) => {
if (error) {
reject(error)
} else {
resolve(results)
}
if (connect) {
client.end()
}
}
)
})
}
class MySQLIntegration extends Sql implements DatasourcePlus { class MySQLIntegration extends Sql implements DatasourcePlus {
private config: MySQLConfig private config: MySQLConfig
private readonly client: any private client: any
public tables: Record<string, Table> = {} public tables: Record<string, Table> = {}
public schemaErrors: Record<string, string> = {} public schemaErrors: Record<string, string> = {}
@ -119,17 +92,46 @@ module MySQLModule {
if (config.ssl && Object.keys(config.ssl).length === 0) { if (config.ssl && Object.keys(config.ssl).length === 0) {
delete config.ssl delete config.ssl
} }
this.client = mysql.createConnection(config) this.config = config
}
async connect() {
this.client = await mysql.createConnection(this.config)
}
async disconnect() {
await this.client.end()
}
async internalQuery(
query: SqlQuery,
connect: boolean = true
): Promise<any[] | any> {
try {
if (connect) {
await this.connect()
}
// Node MySQL is callback based, so we must wrap our call in a promise
const response = await this.client.query(
query.sql,
query.bindings || []
)
return response[0]
} finally {
if (connect) {
await this.disconnect()
}
}
} }
async buildSchema(datasourceId: string, entities: Record<string, Table>) { async buildSchema(datasourceId: string, entities: Record<string, Table>) {
const tables: { [key: string]: Table } = {} const tables: { [key: string]: Table } = {}
const database = this.config.database const database = this.config.database
this.client.connect() await this.connect()
try {
// get the tables first // get the tables first
const tablesResp = await internalQuery( const tablesResp = await this.internalQuery(
this.client,
{ sql: "SHOW TABLES;" }, { sql: "SHOW TABLES;" },
false false
) )
@ -141,14 +143,16 @@ module MySQLModule {
for (let tableName of tableNames) { for (let tableName of tableNames) {
const primaryKeys = [] const primaryKeys = []
const schema: TableSchema = {} const schema: TableSchema = {}
const descResp = await internalQuery( const descResp = await this.internalQuery(
this.client,
{ sql: `DESCRIBE \`${tableName}\`;` }, { sql: `DESCRIBE \`${tableName}\`;` },
false false
) )
for (let column of descResp) { for (let column of descResp) {
const columnName = column.Field const columnName = column.Field
if (column.Key === "PRI" && primaryKeys.indexOf(column.Key) === -1) { if (
column.Key === "PRI" &&
primaryKeys.indexOf(column.Key) === -1
) {
primaryKeys.push(columnName) primaryKeys.push(columnName)
} }
const constraints = { const constraints = {
@ -174,38 +178,41 @@ module MySQLModule {
} }
} }
} }
} finally {
this.client.end() await this.disconnect()
}
const final = finaliseExternalTables(tables, entities) const final = finaliseExternalTables(tables, entities)
this.tables = final.tables this.tables = final.tables
this.schemaErrors = final.errors this.schemaErrors = final.errors
} }
async create(query: SqlQuery | string) { async create(query: SqlQuery | string) {
const results = await internalQuery(this.client, getSqlQuery(query)) const results = await this.internalQuery(getSqlQuery(query))
return results.length ? results : [{ created: true }] return results.length ? results : [{ created: true }]
} }
async read(query: SqlQuery | string) { async read(query: SqlQuery | string) {
return internalQuery(this.client, getSqlQuery(query)) return this.internalQuery(getSqlQuery(query))
} }
async update(query: SqlQuery | string) { async update(query: SqlQuery | string) {
const results = await internalQuery(this.client, getSqlQuery(query)) const results = await this.internalQuery(getSqlQuery(query))
return results.length ? results : [{ updated: true }] return results.length ? results : [{ updated: true }]
} }
async delete(query: SqlQuery | string) { async delete(query: SqlQuery | string) {
const results = await internalQuery(this.client, getSqlQuery(query)) const results = await this.internalQuery(getSqlQuery(query))
return results.length ? results : [{ deleted: true }] return results.length ? results : [{ deleted: true }]
} }
async query(json: QueryJson) { async query(json: QueryJson) {
this.client.connect() await this.connect()
const queryFn = (query: any) => internalQuery(this.client, query, false) try {
const output = await this.queryWithReturning(json, queryFn) const queryFn = (query: any) => this.internalQuery(query, false)
this.client.end() return await this.queryWithReturning(json, queryFn)
return output } finally {
await this.disconnect()
}
} }
} }

View File

@ -40,7 +40,7 @@ const SQL_TYPE_MAP = {
export enum SqlClients { export enum SqlClients {
MS_SQL = "mssql", MS_SQL = "mssql",
POSTGRES = "pg", POSTGRES = "pg",
MY_SQL = "mysql", MY_SQL = "mysql2",
ORACLE = "oracledb", ORACLE = "oracledb",
} }