ndbc-mysql
Version:
The ndbc connector to connect mysql database.
428 lines (359 loc) • 12.2 kB
text/typescript
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 {
}
}