"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var SQLModule_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SQLModule = void 0;
const core_1 = require("@midwayjs/core");
const decorator_1 = require("@midwayjs/decorator");
const magic_datasource_1 = require("../../magic.datasource");
const utils_1 = require("../../utils");
const cache_1 = require("../cache");
const BoundSql_1 = require("./BoundSql");
const dialect_1 = require("./dialect");
const DefaultSqlInterceptor_1 = require("./interceptor/DefaultSqlInterceptor");
const model_1 = require("./model");
const model_2 = require("./model");
const table_1 = require("./table");
/**
 * 事务处理器
 */
class Transaction {
    constructor(queryRunner) {
        this.queryRunner = queryRunner;
    }
    /**
     * 回滚事务
     */
    async rollback() {
        await this.queryRunner.rollbackTransaction();
    }
    /**
     * 提交事务
     */
    async commit() {
        await this.queryRunner.commitTransaction();
    }
}
let SQLModule = SQLModule_1 = class SQLModule {
    /**
     * 添加SQL拦截器
     * @param sqlInterceptor
     */
    static addSqlInterceptor(sqlInterceptor) {
        SQLModule_1.sqlInterceptors.push(sqlInterceptor);
    }
    /**
     * 添加单表crud拦截器
     * @param namedTableInterceptor
     */
    static addNamedTableInterceptor(namedTableInterceptor) {
        SQLModule_1.namedTableInterceptors.push(namedTableInterceptor);
    }
    /**
     * 初始化
     */
    async init() {
        this.logicDeleteColumn = this.magicConfig.crud.logicDeleteColumn;
        this.logicDeleteValue = this.magicConfig.crud.logicDeleteValue;
        this.dataSourceNode = await this.magicDataSource.getDefaultDataSource();
        this.dataSourceNames = this.magicDataSource.getDataSourceNames();
        /**
         * 注册多个数据源
         */
        this.dataSourceNames.forEach(key => {
            const cloneSQLModule = this.cloneSQLModule(this.magicDataSource.getDataSource(key));
            Object.defineProperty(this, key, {
                get() {
                    return cloneSQLModule;
                },
            });
        });
    }
    /**
     * 克隆
     * @param dataSourceNode 数据源
     * @protected
     */
    cloneSQLModule(dataSourceNode) {
        const sqlModule = new SQLModule_1();
        sqlModule.dataSourceNode = dataSourceNode;
        sqlModule.magicDataSource = this.magicDataSource;
        sqlModule.magicConfig = this.magicConfig;
        sqlModule.logicDeleteColumn = this.logicDeleteColumn;
        sqlModule.logicDeleteValue = this.logicDeleteValue;
        sqlModule.ttl = this.ttl;
        sqlModule.cacheName = this.cacheName;
        sqlModule.sqlCache = this.sqlCache;
        return sqlModule;
    }
    /**
     * 数据源名称
     */
    getDataSourceName() {
        return this.dataSourceNode == null
            ? 'unknown'
            : this.dataSourceNode.options['_name'];
    }
    /**
     * 创建查询创建器
     */
    createQueryRunner() {
        if (this.queryRunner == null) {
            this.queryRunner = this.dataSourceNode.createQueryRunner();
        }
    }
    /**
     * 销毁查询创建器
     */
    async release() {
        if (this.queryRunner != null) {
            await this.queryRunner.release();
        }
        if (this.dataSourceNames) {
            for (const key of this.dataSourceNames) {
                this[key].release();
            }
        }
    }
    /**
     * 开启事务
     * @param _function
     */
    async transaction(_function) {
        this.createQueryRunner();
        if (_function) {
            try {
                await this.queryRunner.startTransaction();
                const val = await _function();
                await this.queryRunner.commitTransaction();
                return val;
            }
            catch (e) {
                await this.queryRunner.rollbackTransaction();
                throw e;
            }
            return;
        }
        await this.queryRunner.startTransaction();
        return new Transaction(this.queryRunner);
    }
    /**
     * 配置缓存
     * @param cacheName 缓存名称
     * @param ttl 缓存失效时间 单位为秒
     */
    cache(cacheName, ttl = 0) {
        if ((0, utils_1.isBlank)(cacheName)) {
            throw new core_1.MidwayCommonError('缓存名称不能为空');
        }
        this.cacheName = cacheName;
        this.ttl = ttl;
        return this;
    }
    /**
     * 删除缓存
     * @param cacheName 缓存名称
     * @param matchStart 是否匹配开头为缓存名称的所有key
     */
    async deleteCache(cacheName, matchStart = true) {
        if ((0, utils_1.isNotBlank)(cacheName)) {
            await this.sqlCache.del(cacheName, matchStart);
        }
    }
    /**
     * 查询List，并传入变量信息
     */
    async select(sqlOrXml, params) {
        const boundSql = new BoundSql_1.BoundSql({
            sqlOrXml,
            bindParameters: params,
            sqlModule: this,
        });
        await boundSql.init();
        return await this._select(boundSql);
    }
    /**
     * 查询int值，适合单行单列int的结果
     */
    async selectInt(sqlOrXml, params) {
        const boundSql = new BoundSql_1.BoundSql({
            sqlOrXml,
            bindParameters: params,
            sqlModule: this,
        });
        await boundSql.init();
        return await this._selectInt(boundSql);
    }
    /**
     * 查询单条结果，查不到返回null
     */
    async selectOne(sqlOrXml, params) {
        const boundSql = new BoundSql_1.BoundSql({
            sqlOrXml,
            bindParameters: params,
            sqlModule: this,
        });
        await boundSql.init();
        return await this._selectOne(boundSql);
    }
    /**
     * 查询单行单列的值
     */
    async selectValue(sqlOrXml, params) {
        const boundSql = new BoundSql_1.BoundSql({
            sqlOrXml,
            bindParameters: params,
            sqlModule: this,
        });
        await boundSql.init();
        return await this._selectValue(boundSql);
    }
    async _select(boundSql) {
        this.assertDatasourceNotNull();
        return await boundSql.execute(SQLModule_1.sqlInterceptors, async () => await this.queryForList(boundSql));
    }
    async _selectInt(boundSql) {
        this.assertDatasourceNotNull();
        const value = await this._selectValue(boundSql);
        if (typeof value === 'number') {
            return value;
        }
        else if (typeof value === 'string') {
            try {
                return parseInt(value);
            }
            catch (_a) { }
        }
        throw new core_1.MidwayCommonError(`SQL[${boundSql.getSql()}]：返回值类型不为Number`);
    }
    async _selectOne(boundSql) {
        this.assertDatasourceNotNull();
        return await boundSql.execute(SQLModule_1.sqlInterceptors, async () => {
            this.createQueryRunner();
            const result = await this.queryRunner.query(boundSql.getSql(), boundSql.parameters);
            if (result.length > 0) {
                const values = result[0];
                if (values != null && boundSql.excludeColumns != null) {
                    // list.forEach(row => boundSql.getExcludeColumns().forEach(row::remove));
                    boundSql.excludeColumns.forEach(column => delete values[column]);
                }
                return values;
            }
            return null;
        });
    }
    async _selectValue(boundSql) {
        this.assertDatasourceNotNull();
        return await boundSql.execute(SQLModule_1.sqlInterceptors, async () => {
            this.createQueryRunner();
            const result = await this.queryRunner.query(boundSql.getSql(), boundSql.parameters);
            if (result.length > 0) {
                const values = Object.values(result[0]);
                if (values.length > 0) {
                    return values[0];
                }
            }
            return null;
        });
    }
    /**
     * 分页查询（手动传入limit和offset参数）
     * @param countSqlOrXml count语句
     * @param sqlOrXml `SQL`语句或`xml`
     * @param limit 限制条数
     * @param offset 跳过条数
     * @param params 变量信息
     */
    async page(countSqlOrXml, sqlOrXml, limit, offset, params) {
        if (arguments.length === 3 ||
            (arguments.length === 4 && typeof arguments[1] === 'number')) {
            params = offset;
            offset = limit;
            limit = sqlOrXml;
            sqlOrXml = countSqlOrXml;
            const boundSql = new BoundSql_1.BoundSql({
                sqlOrXml: sqlOrXml,
                bindParameters: params,
                sqlModule: this,
            });
            return await this._page(boundSql, new model_1.Page(limit, offset));
        }
        else {
            const count = await this._selectInt(new BoundSql_1.BoundSql({
                sqlOrXml: countSqlOrXml,
                bindParameters: params,
                sqlModule: this,
            }));
            const boundSql = new BoundSql_1.BoundSql({
                sqlOrXml: sqlOrXml,
                bindParameters: params,
                sqlModule: this,
            });
            return await this.__page(count, boundSql, new model_1.Page(limit, offset), null);
        }
    }
    async __page(count, boundSql, page, dialect) {
        let list = null;
        if (count > 0) {
            if (dialect == null) {
                dialect = dialect_1.DialectAdapter.getDialectFromDataSource(this.dataSourceNode);
            }
            const pageBoundSql = this.buildPageBoundSql(dialect, boundSql, page.offset, page.limit);
            list = await pageBoundSql.execute(SQLModule_1.sqlInterceptors, async () => await this.queryForList(pageBoundSql));
        }
        // RequestEntity requestEntity = RequestContext.getRequestEntity();
        // return resultProvider.buildPageResult(requestEntity, page, count, list);
        return new model_2.PageResult(count, list);
    }
    async _page(boundSql, page) {
        this.assertDatasourceNotNull();
        const dialect = dialect_1.DialectAdapter.getDialectFromDataSource(this.dataSourceNode);
        const countBoundSql = boundSql.copy(dialect.getCountSql(boundSql.getSql()));
        const count = await this._selectInt(countBoundSql);
        return await this.__page(count, boundSql, page, dialect);
    }
    async queryForList(boundSql) {
        this.createQueryRunner();
        const list = await this.queryRunner.query(boundSql.getSql(), boundSql.parameters);
        //   List<Map<String, Object>> list = dataSourceNode.getJdbcTemplate().query(boundSql.getSql(), this.columnMapRowMapper, boundSql.getParameters());
        if (boundSql.excludeColumns != null) {
            // list.forEach(row => boundSql.getExcludeColumns().forEach(row::remove));
            list.forEach(row => boundSql.excludeColumns.forEach(column => delete row[column]));
        }
        return list;
    }
    assertDatasourceNotNull() {
        if (this.dataSourceNode == null) {
            throw new core_1.MidwayCommonError('当前数据源未设置');
        }
    }
    /**
     * 执行update，并传入变量信息
     */
    async update(sqlOrXml, params) {
        const boundSql = new BoundSql_1.BoundSql({
            sqlOrXml,
            bindParameters: params,
            sqlModule: this,
        });
        await boundSql.init();
        return await this._update(boundSql);
    }
    async _update(boundSql) {
        var _a;
        this.assertDatasourceNotNull();
        SQLModule_1.sqlInterceptors.forEach(sqlInterceptor => sqlInterceptor.preHandle(boundSql));
        this.createQueryRunner();
        const result = await this.queryRunner.query(boundSql.getSql(), boundSql.parameters, true);
        let value = result.affected != undefined
            ? result.affected
            : ((_a = result.records) === null || _a === void 0 ? void 0 : _a.length) || 0;
        SQLModule_1.sqlInterceptors.forEach(sqlInterceptor => {
            value = sqlInterceptor.postHandle(boundSql, value);
        });
        return value;
    }
    /**
     * 执行update，并传入变量信息
     */
    async insert(sqlOrXml, params) {
        const boundSql = new BoundSql_1.BoundSql({
            sqlOrXml,
            bindParameters: params,
            sqlModule: this,
        });
        await boundSql.init();
        return await this._insert(boundSql);
    }
    async _insert(boundSql) {
        this.assertDatasourceNotNull();
        SQLModule_1.sqlInterceptors.forEach(sqlInterceptor => sqlInterceptor.preHandle(boundSql));
        this.createQueryRunner();
        let value = await this.queryRunner.query(boundSql.getSql(), boundSql.parameters);
        SQLModule_1.sqlInterceptors.forEach(sqlInterceptor => {
            value = sqlInterceptor.postHandle(boundSql, value);
        });
        await this.deleteCache(this.cacheName);
        return value;
    }
    /**
     * 批量执行操作，返回受影响的行数
     * @param sql `SQL`语句 或者多条sql
     * @param args 变量信息
     * @param batchSize 每次多少条
     */
    async batchUpdate(sql, args, batchSize) {
        this.assertDatasourceNotNull();
        this.createQueryRunner();
        if (arguments.length === 1) {
            const number = await this.update(sql.join(';'));
            await this.deleteCache(this.cacheName);
            return number;
        }
        else if (arguments.length === 2) {
            sql =
                Array.from({ length: args.length })
                    .map(() => sql)
                    .join(';') + ';';
            const parameters = [];
            args.forEach(arg => parameters.push(...arg));
            const number = await this._update(new BoundSql_1.BoundSql({
                sqlOrXml: sql,
                parameters,
                sqlModule: this,
            }));
            await this.deleteCache(this.cacheName);
            return number;
        }
        else {
            const sliceLength = Math.ceil(args.length / batchSize);
            const _batchUpdate = [];
            for (let i = 0; i < sliceLength; i++) {
                const parameters = [];
                const sliceArgs = i === sliceLength - 1
                    ? args.slice(i * batchSize)
                    : args.slice(i * batchSize, batchSize * (i + 1));
                sliceArgs.forEach(arg => parameters.push(...arg));
                _batchUpdate.push(this._update(new BoundSql_1.BoundSql({
                    sqlOrXml: Array.from({ length: sliceArgs.length })
                        .map(() => sql)
                        .join(';') + ';',
                    parameters,
                    sqlModule: this,
                })));
                const result = await Promise.all(_batchUpdate);
                // console.log(result);
                const number = result.reduce((previousValue, currentValue) => previousValue + currentValue);
                await this.deleteCache(this.cacheName);
                return number;
            }
        }
        // await this.queryRunner.query(sqls.join(';'), args);
    }
    /**
     * 指定table，进行单表操作
     * @param tableName
     */
    table(tableName) {
        return new table_1.NamedTable({
            tableName,
            sqlModule: this,
            namedTableInterceptors: SQLModule_1.namedTableInterceptors,
        });
    }
    buildPageBoundSql(dialect, boundSql, offset, limit) {
        const pageSql = dialect.getPageSql(boundSql.getSql(), boundSql, offset, limit);
        return boundSql.copy(pageSql);
    }
};
SQLModule.sqlInterceptors = [
    new DefaultSqlInterceptor_1.DefaultSqlInterceptor(),
];
SQLModule.namedTableInterceptors = [];
__decorate([
    (0, decorator_1.Inject)(),
    __metadata("design:type", magic_datasource_1.MagicDataSource)
], SQLModule.prototype, "magicDataSource", void 0);
__decorate([
    (0, decorator_1.Inject)(),
    __metadata("design:type", cache_1.CacheModule)
], SQLModule.prototype, "sqlCache", void 0);
__decorate([
    (0, decorator_1.Inject)(),
    __metadata("design:type", Object)
], SQLModule.prototype, "ctx", void 0);
__decorate([
    (0, decorator_1.Config)('magic'),
    __metadata("design:type", Object)
], SQLModule.prototype, "magicConfig", void 0);
__decorate([
    (0, decorator_1.Init)(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], SQLModule.prototype, "init", null);
SQLModule = SQLModule_1 = __decorate([
    (0, decorator_1.Provide)()
], SQLModule);
exports.SQLModule = SQLModule;
//# sourceMappingURL=SQLModule.js.map