UNPKG

sqlite-plugin-red

Version:

Adding a tab to show and edit the structure of a sqlite database. Needs node-red-node-sqlite.

124 lines (116 loc) 4.84 kB
const Database = require('better-sqlite3') const getStructure = (path) => { const db = new Database(path) const tables = db.prepare('SELECT name, sql FROM sqlite_master WHERE type=? AND name NOT LIKE ?') const structure = [] for (const table of tables.iterate('table', 'sqlite_%')) { const tableObject = { name: table.name } // autoincrement info can only received by table sql. We could also retrieve all neccessary table infos from here, // but it must be filtered from a string... we only do this here as AUTONINCREMENT can only be set once (and then there is only one primary key) if (table.sql.includes('PRIMARY KEY') && table.sql.includes('AUTOINCREMENT')) { tableObject.autoincrement = table.sql.split('PRIMARY KEY(')[1].split('"')[1] } structure.push(tableObject) } // ...therefore we get the column data from pragma table_info structure.forEach((table, index) => { const columns = db.prepare('PRAGMA table_info( "' + table.name + '" );').all() // and change number to bool for checkbox using later columns.forEach(c => { if (c.notnull === 0) c.notnull = false else c.notnull = true if (c.pk === 0) c.pk = false else c.notnull = true c.ai = false c.unique = false }) // ... and add the autoincrement there if (table.autoincrement) { const autoIncrIndex = columns.findIndex(c => c.name === table.autoincrement) if (autoIncrIndex !== -1) { columns[autoIncrIndex].ai = true delete table.autoincrement } } // ... as well as if the column is unique const uniqueFields = db.prepare('SELECT DISTINCT m.name as table_name, ii.name as column_name FROM sqlite_master AS m, pragma_index_list(m.name) AS il, pragma_index_info(il.name) AS ii WHERE m.type=? AND il.[unique] = 1;').bind('table').all() const uniqueFieldsInThisTable = uniqueFields.filter(f => f.table_name === table.name) uniqueFieldsInThisTable.forEach(uField => { const uFieldIndex = columns.findIndex(c => c.name === uField.column_name) if (uFieldIndex > -1) columns[uFieldIndex].unique = true }) structure[index].columns = columns }) db.close() return structure } const runSql = (path, sql, command) => { const db = new Database(path) const statement = db.prepare(sql) let result try { if (command === 'run') { result = statement.run() } else if (command === 'getAll') { result = statement.all() if (typeof result === 'undefined') result = {} } else if (command === 'get') { result = statement.get() if (typeof result === 'undefined') result = {} } else { throw new Error('No command stated!') } return result } catch(e) { throw new Error(e) } } const createTableWithNewProp = (path, fields, fieldsSql, backupName) => { const tempBackup = backupName + '_temp_' + Date.now() + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) createCopyDb(path, fields, fieldsSql, backupName, tempBackup) commitCopyDb(path, backupName, tempBackup) } const createCopyDb = (path, fields, fieldsSql, tableName, backupName) => { const db = new Database(path) if (!tableName || !backupName) throw new Error('Missing table name!') try { // Steps for altering column props as stated in https://www.sqlite.org/lang_altertable.html // Create new table db.prepare(`CREATE TABLE "${backupName}" ${fieldsSql}`).run() // Copy data db.prepare(`INSERT INTO "${backupName}" (${fields}) SELECT ${fields} FROM "${tableName}"`).run() return 'db copy' } catch (e) { db.prepare('DROP TABLE IF EXISTS "' + backupName + '"').run() throw new Error(e) } } const commitCopyDb = (path, tableName, backupName) => { // to change a column property we have to go through the following steps... an alter table column will propably never be implemented (expect name changing) const db = new Database(path) if (!tableName || !backupName) throw new Error('Missing table name!') try { // Drop old table (first check if data was copied!) const getFirstLineOrg = db.prepare('SELECT * FROM "' + tableName + '"').get() const getFirstLineCopy = db.prepare('SELECT * FROM "' + backupName + '"').get() if (getFirstLineOrg && !getFirstLineCopy) { throw new Error('Something went wrong! Database copy is missing data -> Reset all db changes.') } else { db.prepare('DROP TABLE "' + tableName + '"').run() // Rename new into old db.prepare(`ALTER TABLE "${backupName}" RENAME TO "${tableName}";`).run() db.close() return 'Table changed' } } catch (e) { db.prepare('DROP TABLE IF EXISTS "' + backupName + '"').run() throw new Error(e) } } module.exports = { getStructure, runSql, createTableWithNewProp, createCopyDb, commitCopyDb }