import fs from "fs"; import {logger} from "../logger/Logger"; import Singleton from "../patterns/Singleton"; import Bean from "../decorators/Bean"; import FileUtil from "../utils/FileUtil"; import {format, FormatOptions} from 'sql-formatter' import {QueryModeMybatisParser} from "./mybatis/QueryModeMybatisParser"; import {QueryUtils} from "./mybatis/QueryUtils"; const HTML = require('html-parse-stringify2'); type BIND_TYPE = "TEXT" | "VALUE" | "FUNCTION" | "UTIL"; export interface MybatisParseResult { statement: string, param: object, bindParams: object, } @Bean() export default class QueryMapper extends Singleton { public SQL_MAP: any = {}; public loadSync(path: string) { const rootPath: string = path; let sqlFiles: any = []; let fileList: Array = fs.readdirSync(rootPath); for (let idx = 0; idx < fileList.length; idx++) { let fileName = fileList[idx]; const filePath: string = rootPath + "/" + fileName; if (fileName.lastIndexOf(".xml") < 0) { continue; } sqlFiles.push(filePath); } sqlFiles.forEach((element: any) => { this.createMapper(element); logger.info("load query:", element); }); }; public load(path: string) { const rootPath: string = path; const that = this; let sqlFiles: any = []; fs.readdir(rootPath, function (error, filelist) { filelist.forEach(fileName => { const filePath: string = rootPath + "/" + fileName; if (fileName.lastIndexOf(".xml") > -1) { sqlFiles.push(filePath); } }); }); sqlFiles.forEach((element: any) => { this.createMapper(element); logger.info("load query:", element); }); }; private createMapper(xml: string) { let mappers: any[] = []; // Parse each XML files try { let rawText = QueryUtils.replaceCdata(FileUtil.readString(xml)).toString(); mappers = HTML.parse(rawText); } catch (err) { console.log(err); throw new Error("Error occured during open XML file [" + xml + "]"); } try { for (let j = 0, mapper; mapper = mappers[j]; j++) { // Mapping tag recursively this.findMapper(mapper); } } catch (err) { throw new Error("Error occured during parse XML file [" + xml + "]"); } } private findMapper(children: any) { let queryTypes: any[] = ['sql', 'select', 'insert', 'update', 'delete', 'query']; if (children.type == 'tag' && children.name == 'mapper') { //Add Mapper when existed namespace then add children sqls let namespaceVal = children.attrs.namespace; if (!this.SQL_MAP[namespaceVal]) { this.SQL_MAP[namespaceVal] = {} } for (let j = 0, sql; sql = children.children[j]; j++) { if (sql['type'] == 'tag' && queryTypes.indexOf(sql['name']) > -1) { this.SQL_MAP[namespaceVal][sql.attrs.id] = sql.children } } return; } else { if (children['children'] != null && children['children'].length > 0) { for (let j = 0, nextChildren; nextChildren = children.children[j]; j++) { this.findMapper(nextChildren); } } else { return; } } } public getStatementByStringParse(namespace: string, sqlid: string, formatObj: any, param: any, dynamicCondition: any) { let statement: string = ''; try { for (let i = 0, children; children = this.SQL_MAP[namespace][sqlid][i]; i++) { // Convert SQL statement recursively param = !param ? {} : param statement += new QueryModeMybatisParser().convertChildren(children, param, namespace, this.SQL_MAP, dynamicCondition); } } catch (err) { logger.error(`mybatis parser getStatement error : [${err}]`) throw err; } try { if (formatObj != undefined && formatObj != null) { statement = format(statement, formatObj as any); } } catch (err) { logger.error(`Sql foramtter error : [${err}]`) throw err; } return statement; } public getStatementByBindParse(namespace: string, sqlid: string, formatObj: any, param: any, dynamicCondition: any) { let statement: string = ''; let bindParams = {}; try { for (let i = 0, children; children = this.SQL_MAP[namespace][sqlid][i]; i++) { // Convert SQL statement recursively param = !param ? {} : param // statement += new BindModeMybatisParser().convertChildren(children, param, namespace, this.SQL_MAP, bindParams, dynamicCondition); } } catch (err) { logger.error(`mybatis parser getStatement error:[${err}]`) throw err; } try { if (formatObj != undefined && formatObj != null) { statement = format(statement, formatObj as any); } } catch (err) { logger.error(`Sql foramtter error : [${err}]`) throw err; } return { statement: statement, param: param, bindParams: bindParams, }; } public getStatement(sqlId: string, formatObj: any, param?: any, dynamicCondition?: any): string | MybatisParseResult { let namespace: string = sqlId.substring(0, sqlId.indexOf(".")); let sqlid: string = sqlId.substring(sqlId.indexOf(".") + 1, sqlId.length); // mssql // const SQL_FORMATTER_OPTION:FormatOptions = {language: 'tsql', indent: ' '}; // oracle // const SQL_FORMATTER_OPTION:FormatOptions = {language: 'plsql', indent: ' '}; // mysql // const SQL_FORMATTER_OPTION:FormatOptions = {language: 'mysql', indent: ' '}; // mariadb // const SQL_FORMATTER_OPTION:FormatOptions = {language: 'mariadb', indent: ' '}; if (!namespace || namespace == null) throw new Error('Namespace should not be null.'); if (this.SQL_MAP[namespace] == undefined) throw new Error('Namespace [' + namespace + '] not exists.'); if (sqlid == null) throw new Error('SQL ID should not be null.'); if (this.SQL_MAP[namespace][sqlid] == undefined) throw new Error('SQL ID [' + sqlid + '] not exists'); if (formatObj.language === "mysql") { return this.getStatementByStringParse(namespace, sqlid, formatObj, param, dynamicCondition) } else { return this.getStatementByBindParse(namespace, sqlid, formatObj, param, dynamicCondition) } } }