猫史档案馆


NomenSQLitePro1.0:表格字段自动更新 - 为开发者提供的便捷调试工具

用户:NomenNomen查看:1 回复:2 评论:1 创建时间:2023-05-11T13:20:31


这里是没有开场白的Nomen, NomenSQLitePro终于写好了。

这是一个特殊的工具,它唯一多出来的功能就是表格字段自动更新(其实有重命名表格和重命名列,但是被我砍掉了)

同时,它也是一个为框架开发者着想的调试工具,人性化地提供了输出功能,只需要在Config里面调整一下就可以开启三种输出:INFO,WARNING,DEBUG,分别是信息,警告,调试(错误信息会强制输出,但是不会中断代码执行)

修修补补,解决了一些历史遗留问题之后,我们来简单讲讲这一次更新的重要功能:自动更新字段

其原理相当简单:挪动到一个新建的表格里,然后删掉旧表格,然后重命名新表格为旧表格。当你启用了autoUpdateColumn之后,每次启动NomenSQLitePro就会自动扫描一遍数据结构,然后如果出现不相同的情况的时候,挪动旧表格(请注意,在这个过程中,可能会出现新的数据与旧数据不统一

1.如果多的字段,则新表格也会带有这个新字段

2.如果少了字段,那么新表格会丢失这个字段上所有的数据(所以,修改传入的config的时候,千万千万一定要检查以下是不是漏删什么数据,NomenSQLitePro不带有FireWall功能,不能进行卷轴回滚,如果误删了千万别怪我(跑路))

3.字段改名字了,那么会出现一个新字段为当前的名字,而旧字段被丢失(不要改名)

本体内容:

class NomenSQLitePro {
    constructor() {
        if (NomenSQLitePro.instance) return NomenSQLitePro.instance;
        NomenSQLitePro.instance = this;
    }
    async update() {
        function parseSql(sql) {
            let _sql = sql.match(/\(([\s\S]*)\)/)[1].replace(/\n/g, '');
            const regex = /"([^"]+)"\s+([^,]+)(?:,|$)/g;
            const result = [];
            let match;
            while ((match = regex.exec(_sql)) !== null) {
                result.push({ name: match[1], rule: match[2] });
            }
            return result;
        }
        function diffArray(s, f) {
            return s.concat(f).reduce(function (c, r) {
                if (!s.includes(r) || !f.includes(r)) c.push(r);
                return c;
            }, []);
        }
        let result = await db.sql([`SELECT * FROM sqlite_master`]);
        if (NomenSQLitePro.info) console.log(`[INFO]Start Update`);
        for (let i = 0; i < result.length; i++) {
            if (NomenSQLitePro._data[result[i].name] && diffArray(parseSql(result[i].sql).map(v => v.name), NomenSQLitePro._data[result[i].name].map(v => v.name)).length) {
                if (NomenSQLitePro.debug) console.log(`[DEBUG]${result[i].name} ${JSON.stringify(parseSql(result[i].sql))} ${result[i].sql}`)
                await this._updateColumn(NomenSQLitePro._data[result[i].name], result[i].name);
            }
        }
        if (NomenSQLitePro.info) console.log(`[INFO]Update Finish`)
    };
    static launched = false;
    static debug = false;
    static async launch(config, action, callback) {
        if (!config) throw new Error('请传入配置参数后重试');
        if (!this.instance) this.instance = new NomenSQLitePro();
        else return this.instance;
        let time = Date.now()
        this.tables = config.tables || {};
        this._data = {};
        this.debug = config.debug || false;
        this.warn = config.warn || false;
        this.info = config.info || false;
        if (!Object.keys(this.tables).length) this._data = null;
        Object.keys(this.tables).forEach(v => {
            if (Array.isArray(this.tables[v]) && this.tables[v].length) {
                this._data[v] = [];
                this.tables[v].forEach(r => {
                    if (typeof r.name == "string" && Object.values(this.DataType).includes(r.type)) {
                        this._data[v].push({ name: r.name, type: r.type, notnull: r.notnull || false });
                    }
                })
            }
        })
        if (NomenSQLitePro.debug) console.log(`[DEBUG]${JSON.stringify(this._data)}`);
        if (!Object.keys(this._data).length) return this.instance;
        let errMessage = null;
        if (typeof action == "string" && action.includes('create')) {
            for (let i = 0; i < Object.keys(this._data).length; i++) {
                let eString = `CREATE TABLE IF NOT EXISTS "${Object.keys(this._data)[i]}" 
                (${this._data[Object.keys(this._data)[i]].reduce((p, c) => {
                    let str = `"${c.name}" ${c.type} ${c.notnull ? 'NOT NULL' : ''}`;
                    return p ? p.concat(',', str) : str;
                }, false)})`;
                try { await this.instance._exec(eString); } catch (err) {
                    errMessage.concat(err);
                }
            }
        }
        if (config.autoUpdateColumn) {
            try { await this.instance.update() } catch (err) {
                console.error(`[ERROR]SQL NomenSQLitePro Error: ${err}`)
            }
        }
        this.launched = true;
        if (typeof callback == "function") callback(null, errMessage);
        if (NomenSQLitePro.info) console.log(`[INFO] NomenSQLitePro启动完成 耗时:${Date.now() - time}ms`)
        return this.instance;
    }
    _exec(cmd) {
        return (typeof cmd == 'string') ? db.sql([cmd]) : new Promise(resolve => resolve(new TypeError('传入参数有误,预期应该为string')))
    }
    getAllTables() {
        return Object.keys(NomenSQLitePro._data);
    }
    async getAllTablesInDatabase() {
        return (await this._exec(`SELECT * FROM sqlite_master`)).map(v => v.name)
    }
    async _updateColumn(data, name) {
        function g(length) {
            var result = '';
            var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
            for (var i = 0; i < length; i++) result += characters.charAt(Math.floor(Math.random() * characters.length));
            return result;
        }
        let tbName = g(16);
        await this._exec(`BEGIN TRANSACTION`);
        if (NomenSQLitePro.warn) console.warn(`[WARNING]SQL TABLE ${name} 已被 NomenSQLitePro(${tbName})锁定`)
        await this._exec(`CREATE TABLE IF NOT EXISTS "${tbName}" (${data.reduce((p, c) => {
            let str = `"${c.name}" ${c.type} ${c.notnull ? 'NOT NULL' : ''}`;
            return p ? p.concat(',', str) : str;
        }, false)});`);
        await this._exec(`INSERT INTO ${tbName} SELECT ${data.reduce((p, c) => {
            let str = `"${c.name}"`;
            return p ? p.concat(',', str) : str;
        }, false)} FROM ${name};`);
        await this._exec(`DROP TABLE ${name};`);
        await this._exec(`ALTER TABLE ${tbName} RENAME TO ${name};`);
        await this._exec(`COMMIT`);
        if (NomenSQLitePro.warn) console.warn(`[WARNING]SQL TABLE ${name} 已重写并释放`)
    }
    operate(name) {
        let t = this;
        if (typeof name == "string" && NomenSQLitePro.launched) return {
            select(column, value) {
                return t._exec(`SELECT * FROM "${name}" ${column == "*" ? `` : (`WHERE "${column}" = ` + `"${value}"`)}`)
            },
            remove(column, value) {
                return t._exec(`DELETE FROM "${name}" ${column == "*" ? `` : (`WHERE "${column}" = ` + `"${value}"`)}`)
            },
            insert(value) {
                if (Object.prototype.toString.call(value) == '[object Object]' && Object.keys(value).length) {
                    let eString = `(${Object.keys(value).map(v => `"${v}"`).join(',')}) VALUES(${Object.values(value).map(v => `"${v}"`).join(',')})`;
                    return t._exec(`INSERT INTO "${name}" ${eString}`);
                } else return null;
            },
            update(column, value) {
                if (Object.prototype.toString.call(value) == '[object Object]' && Object.keys(value).length && typeof column == "string" && column.includes('=')) {
                    let eArr = [];
                    Object.entries(value).forEach(([k, v]) => eArr.push(`"${k}" = "${v}"`));
                    let aString = `${column == "*" ? "" : 'WHERE "' + column.split('=')[0] + '"="' + column.split('=')[1] + '"'}`;
                    return t._exec(`UPDATE "${name}" SET ${eArr} ${aString}`);
                } else return null;
            }
        }
        return {};
    }
    static instance = null;
    static LaunchAction = {
        CREATE_OR_LAUNCH: 'createlaunch',
        LAUNCH: 'launch'
    }
    static DataType = {
        NUMBER: 'INTEGER',
        FLOAT: 'REAL',
        TEXT: 'TEXT',
        NULL: 'NULL',
        BLOB: 'BLOB'
    }
}

启动示例(async):

const tablesData = {
    'Nomenawa2': [
        { name: 'userkey', type: NomenSQLitePro.DataType.TEXT, notnull: true },
        { name: 'coin', type: NomenSQLitePro.DataType.NUMBER },
        { name: 'bag', type: NomenSQLitePro.DataType.TEXT }
    ],
    'Nomenawa3': [
        { name: 'userkeyawa4', type: NomenSQLitePro.DataType.TEXT, notnull: true },
        { name: 'coin2', type: NomenSQLitePro.DataType.NUMBER },
        { name: 'bag', type: NomenSQLitePro.DataType.TEXT }
    ],
};
const NomenSQLiteProConfig = {
    debug: false, /* 是否显示调试信息 */
    warn: false, /* 是否显示警告信息 */
    info: false, /* 是否显示标识信息 */
    autoUpdateColumn: false, /* 是否自动更新表格 */
    tables: tablesData, /* 传入表格结构 */
};


(async) await NomenSQLitePro.launch(NomenSQLiteProConfig, NomenSQLitePro.LaunchAction.CREATE_OR_LAUNCH, (e, r) => console.log(r));


回复

上一页1 页 / 共 1下一页
NomenNomen

照例沙发

点赞0


评论


神岛吉吉喵神岛吉吉喵

照例板凳

点赞0


评论