UNPKG

ndbc-mysql

Version:

The ndbc connector to connect mysql database.

428 lines (359 loc) 12.2 kB
import {Driver, Connection, Statement, ResultSet, DatabaseMetaData, ColumnInfo, PropertyInfo} from "ndbc-connector"; import * as mysql from "mysql"; import {Utils} from "./src/uitls"; export class MySQLDriver implements Driver { connect(properties: Map<string, any>): Promise<Connection> { let connection:MySQLConnection = new MySQLConnection(properties); return connection.open(); } } export class MySQLConnection implements Connection { private _properties: Map<string, any> = new Map<string, any>(); public _connection!: mysql.Connection; constructor(private readonly properties: Map<string, any>) { this._properties = properties; } open(): Promise<Connection> { return new Promise<Connection>((resolve, rejects) => { try { let user: any = this.getProperty("user", true); let host: any = this.getProperty("host", true); let password: any = this.getProperty("password", true); let database: any = this.getProperty("database", true); let port: any = this.getProperty("port", false, 3306); var connection = mysql.createConnection({ host : host, user : user, password : password, database : database, port : port }); connection.connect((error) => { rejects(error); }); this._connection = connection; resolve(this); } catch (err) { rejects(err); } }); } close(): void { this._connection.end(); } createStatement(query: string): Statement { return new MySQLStatement(this, query); } getMetadata(): DatabaseMetaData { return new MySQLDatabaseMetaData(this); } private getProperty(propertyName: string, required: boolean, defaultValue?: any): any { let value = this._properties.get(propertyName); if (value == undefined || !(typeof value === "string")) { if (required) { throw new Error(`The ${propertyName} is required.`); } else { value = defaultValue; } } return value; } } export class MySQLStatement implements Statement { constructor(private readonly connection: MySQLConnection, private readonly queryString: string) { } execute(parameters: Map<string, any>): Promise<ResultSet> { let values:any[] = []; parameters.forEach((value:any, key:string) => { values.push(value); }); return new Promise((resolve, rejects) => this.connection._connection.query({sql: this.queryString, values: values}, (err, results, fields?: mysql.FieldInfo[]) => { if (err === undefined || err == null) { resolve(new MySQLResultSet(results, fields)); } else { rejects(err); } })); } executeNonQuery(parameters: Map<string, any>): Promise<number> { let values:any[] = []; parameters.forEach((key:string, value:any) => { values.push(value); }); return new Promise((resolve, rejects) => this.connection._connection.query({sql: this.queryString, values: values}, (err, results, fields?: mysql.FieldInfo[]) => { if (err === undefined || err == null) { resolve(1); } else { rejects(err); } })); } } export class MySQLDatabaseMetaData implements DatabaseMetaData { private _properties: PropertyInfo[]; constructor(private readonly connection: MySQLConnection) { this._properties = []; } getConnectionProperites(): PropertyInfo[] { if (this._properties.length === 0) { this._properties.push(new PropertyInfo("host", "string", "", "")) this._properties.push(new PropertyInfo("port", "string", 3306, "")) this._properties.push(new PropertyInfo("database", "string", "", "")) this._properties.push(new PropertyInfo("user", "string", "", "")) this._properties.push(new PropertyInfo("password", "string", "", "")) } return this._properties; } getClientInfoProperties(): Promise<ResultSet> { return new Promise<ResultSet>((resolve, rejects) => { resolve(new EmptyResultSet()); }) } getServerInfoProperties(): Promise<ResultSet> { return new Promise<ResultSet>((resolve, rejects) => { resolve(new EmptyResultSet()); }) } getCatalogs(catalog: string): Promise<ResultSet> { return new Promise<ResultSet>((resolve, rejects) => { resolve(new EmptyResultSet()); }) } async getSchemas(catalog: string, schema: string): Promise<ResultSet> { let statement:Statement = this.connection.createStatement("SHOW DATABASES"); let results:ResultSet | undefined; let metadata:ColumnInfo[] = []; let rows:any[][] = []; try { results = await statement.execute(new Map()); metadata.push(new ColumnInfo("TABLE_SCHEM", "string")); while(results.next()) { let row:any[] = []; row.push(results.getValue(0)); rows.push(row); } } finally { if (results != undefined) { results.close(); } } return new Promise<ResultSet>((resolve, rejects) => { resolve(new ValuesResultSet(metadata, rows)); }) } getTables(catalog: string, schema: string, table: string, types: string[]): Promise<ResultSet> { let query:string = "SELECT TABLE_CATALOG AS TABLE_CAT, TABLE_SCHEMA AS TABLE_SCHEM, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES"; let whereClause:string = ""; let parameters:Map<string, any> = new Map(); if (!Utils.isNullOrEmpty(schema)) { whereClause += " WHERE TABLE_SCHEMA = ? " parameters.set("TABLE_SCHEMA", schema); } if (!Utils.isNullOrEmpty(table)) { if (whereClause === "") { whereClause += " WHERE "; } else { whereClause += " AND "; } whereClause += " TABLE_NAME = ? "; parameters.set("TABLE_NAME", table); } let statement:Statement = this.connection.createStatement(query + whereClause); return statement.execute(parameters); } getColumns(catalog: string, schema: string, table: string): Promise<ResultSet> { let query:string = "SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, NUMERIC_SCALE, EXTRA, COLUMN_COMMENT " + "FROM INFORMATION_SCHEMA.COLUMNS"; let whereClause:string = ""; let parameters:Map<string, any> = new Map(); if (!Utils.isNullOrEmpty(schema)) { whereClause += " WHERE TABLE_SCHEMA = ? " parameters.set("TABLE_SCHEMA", schema); } if (!Utils.isNullOrEmpty(table)) { if (whereClause === "") { whereClause += " WHERE "; } else { whereClause += " AND "; } whereClause += " TABLE_NAME = ? "; parameters.set("TABLE_NAME", table); } let statement:Statement = this.connection.createStatement(query + whereClause); return statement.execute(parameters); } getProcedures(catalog: string, schema: string): Promise<ResultSet> { throw new Error("Method not implemented."); } getProcedureParameters(catalog: string, schema: string, procedure: string): Promise<ResultSet> { throw new Error("Method not implemented."); } } class EmptyResultSet implements ResultSet { close(): void { } next(): boolean { return false; } getValue(index: string | number) { return null; } getMetadata(): ColumnInfo[] { return []; } } class ResultSetBase implements ResultSet { protected _index:number; protected _count:number; constructor() { this._index = -1; this._count = 0; } close(): void { } getValue(index: string | number):any { return null; } getMetadata(): ColumnInfo[] { return []; } next(): boolean { if (this._count <= 0) { return false; } if (this._index < this._count - 1) { this._index ++; return true; } else { this._index = this._count; return false; } } protected ensureRange() { if (this._index < 0) { throw new Error("Please invoke next method first."); } if (this._index >= this._count) { throw new Error("The index is out of range."); } } } class ValuesResultSet extends ResultSetBase { private _columnNames:string[]; constructor(private readonly columns:ColumnInfo[], private readonly results:any[][]){ super(); this._count = results.length; this._columnNames=[]; } getValue(columnName: string | number) { super.ensureRange(); let row:any[] = this.results[this._index]; let index: number = -1; if (typeof columnName === "string") { if (this._columnNames.length == 0) { for (let index = 0; index < this.columns.length; index++) { const element = this.columns[index]; this._columnNames.push(element.getColumnName()); } } index = this._columnNames.indexOf(<string>columnName); } else { index = columnName as number; } if (index == -1) { throw Error(`Invalid of ${columnName} in getValue method`); } return row[index]; } getMetadata(): ColumnInfo[] { return this.columns; } } class KeyValueResultSet extends ResultSetBase { private _metadata:ColumnInfo[]; constructor(private readonly names:string[], private readonly values:string[]) { super(); this._metadata = []; this._count = 1; } getValue(columnName: string | number) { this.ensureRange(); let index: number = -1; if (typeof columnName === "string") { index = this.names.indexOf(columnName); } else { index = columnName as number; } if (index == -1) { throw Error(`Invalid of ${columnName} in getValue method`); } return this.values[index]; } getMetadata(): ColumnInfo[] { if (this._metadata.length == 0) { for (let index = 0; index < this.names.length; index++) { const name = this.names[index]; this._metadata.push(new ColumnInfo(name, "string")); } } return this._metadata; } } export class MySQLResultSet extends ResultSetBase { private _columns: ColumnInfo[] | undefined; private _indexes: string[] | undefined; private _currentRow: any | undefined; constructor(private readonly results: any, private readonly fields: mysql.FieldInfo[] | undefined) { super(); this._count = this.results === undefined ? 0 : this.results.length; } next(): boolean { let value:boolean = super.next(); this._currentRow = undefined; return value; } getValue(columnName: string | number) { this.ensureRange(); if (this._currentRow === undefined) { let rowObject: any = this.results[this._index]; this._currentRow = rowObject; } if (this._currentRow === undefined) { return null; } let name: string | undefined; if (typeof columnName === "number") { if (this._indexes == undefined) { this.getMetadata(); } name = (<string[]>this._indexes)[<number>columnName]; } else { name = columnName as string; } if (name !== undefined) { return this._currentRow[name]; } else { return null; } } getMetadata(): ColumnInfo[] { if (this._columns === undefined) { this._columns = []; this._indexes = []; if (this.fields !== undefined) { this.fields.forEach(element => { if (this._columns != undefined) { let column:ColumnInfo = new ColumnInfo(element.name, element.type.toString()); this._columns.push(column); } if (this._indexes != undefined) { this._indexes.push(element.name); } }); } } return this._columns; } close():void { } }