UNPKG

@capacitor-community/sqlite

Version:

Community plugin for native & electron SQLite databases

1,445 lines (1,415 loc) 231 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var require$$0 = require('node:fs/promises'); var require$$1 = require('path'); var require$$2 = require('fs'); var require$$3 = require('node-fetch'); var require$$4 = require('os'); var require$$5 = require('jszip'); var require$$6 = require('electron'); var require$$4$1 = require('better-sqlite3-multiple-ciphers'); var require$$3$1 = require('electron-json-storage'); var require$$1$1 = require('crypto'); var require$$2$1 = require('crypto-js'); function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var src = {}; var GlobalSQLite = {}; var hasRequiredGlobalSQLite; function requireGlobalSQLite () { if (hasRequiredGlobalSQLite) return GlobalSQLite; hasRequiredGlobalSQLite = 1; Object.defineProperty(GlobalSQLite, "__esModule", { value: true }); GlobalSQLite.GlobalSQLite = undefined; let GlobalSQLite$1 = class GlobalSQLite { constructor() { this.secret = 'sqlite secret'; this.newsecret = 'sqlite new secret'; } }; GlobalSQLite.GlobalSQLite = GlobalSQLite$1; return GlobalSQLite; } var Database = {}; var exportToJson = {}; var utilsSQLite = {}; var UtilsSQL92Compatibility = {}; var hasRequiredUtilsSQL92Compatibility; function requireUtilsSQL92Compatibility () { if (hasRequiredUtilsSQL92Compatibility) return UtilsSQL92Compatibility; hasRequiredUtilsSQL92Compatibility = 1; Object.defineProperty(UtilsSQL92Compatibility, "__esModule", { value: true }); UtilsSQL92Compatibility.UtilsSQL92Compatibility = undefined; let UtilsSQL92Compatibility$1 = class UtilsSQL92Compatibility { compatibleSQL92(statement) { let newStatement = ''; const action = statement.trim().split(' ')[0].toUpperCase(); switch (action) { case 'INSERT': newStatement = this.insertSQL92(statement); break; case 'UPDATE': newStatement = this.updateSQL92(statement); break; case 'DELETE': case 'SELECT': newStatement = this.whereSQL92(statement); break; default: throw new Error(`Error: ${action} not implemented`); } return newStatement; } insertSQL92(insertStatement) { // Split the statement into parts const inStmt = insertStatement.trim(); const valuesStartIndex = inStmt.indexOf('VALUES'); const columnsPart = inStmt.substring(0, valuesStartIndex); const valuesPart = inStmt.substring(valuesStartIndex); // Extract values and replace double quotes with single quotes const modifiedValuesPart = valuesPart.replace(/"([^"]+)"/g, "'$1'"); // Reconstruct the modified statement const modifiedStatement = columnsPart + modifiedValuesPart; return modifiedStatement; } updateSQL92(updateStatement) { // Split the statement into SET and WHERE parts let isWhere = true; const setWhereSplit = updateStatement.toUpperCase().split('WHERE'); if (setWhereSplit.length <= 1) isWhere = false; const setUpdate = setWhereSplit[0].toUpperCase().split('SET')[0].trim(); const setPart = setWhereSplit[0].toUpperCase().split('SET')[1].trim(); const modifiedSetPart = this.modSetPart(setPart); let modifiedStatement = `${setUpdate} SET ${modifiedSetPart}`; if (isWhere) { for (let i = 1; i < setWhereSplit.length; i++) { const wherePart = setWhereSplit[i].trim(); const modifiedWherePart = this.modWherePart(wherePart); modifiedStatement += ` WHERE ${modifiedWherePart}`; } } return modifiedStatement; } whereSQL92(statement) { // Split the statement into SET and WHERE parts const setWhereSplit = statement.toUpperCase().split('WHERE'); if (setWhereSplit.length <= 1) return statement; let modifiedStatement = `${setWhereSplit[0].trim()}`; for (let i = 1; i < setWhereSplit.length; i++) { const wherePart = setWhereSplit[1].trim(); const modifiedWherePart = this.modWherePart(wherePart); modifiedStatement += ` WHERE ${modifiedWherePart}`; } return modifiedStatement; } modSetPart(setStatement) { const commaPart = setStatement.split(','); const modCommaPart = []; for (const com of commaPart) { const equalPart = com.split('='); const value = equalPart[1].replaceAll(`"`, `'`); modCommaPart.push(`${equalPart[0].trim()} = ${value.trim()}`); } return modCommaPart.toString(); } modWherePart(whereStatement) { const keywords = new Set([ '=', '<>', '>', '>=', '<', '<=', 'IN', 'VALUES', '(', ',', ')', 'BETWEEN', 'LIKE', 'AND', 'OR', 'NOT', ]); const newTokens = []; const tokens = whereStatement .split(/(\s|,|\(|\))/) .filter((item) => item !== ' ') .filter((item) => item !== ''); let inClause = false; let inValues = false; let modValue = false; let betwClause = false; let opsClause = false; let inValValues = false; let inValPar = false; for (const token of tokens) { if (new Set(['=', '<>', '>', '>=', '<', '<=']).has(token)) { newTokens.push(token); modValue = true; opsClause = false; } else if (token.toUpperCase() === 'BETWEEN') { newTokens.push(token); betwClause = true; modValue = true; opsClause = false; } else if (betwClause && token.toUpperCase() === 'AND') { newTokens.push(token); modValue = true; betwClause = false; } else if (token.toUpperCase() === 'LIKE') { newTokens.push(token); opsClause = false; modValue = true; } else if (token.toUpperCase() === 'AND' || token.toUpperCase() === 'OR' || token.toUpperCase() === 'NOT') { newTokens.push(token); opsClause = true; } else if (token.toUpperCase() === 'IN') { newTokens.push(token); opsClause = false; inClause = true; } else if (inClause && token === '(') { newTokens.push(token); modValue = true; inValues = true; } else if (inValues && token.toUpperCase() === ',') { newTokens.push(token); modValue = true; } else if (inValues && token.toUpperCase() === 'VALUES') { newTokens.push(token); inValues = false; inValValues = true; inClause = false; } else if (inValValues && token === '(') { newTokens.push(token); inValPar = true; modValue = true; } else if (inValPar && token.toUpperCase() === ',') { newTokens.push(token); modValue = true; } else if (inValPar && inValValues && token === ')') { newTokens.push(token); inValPar = false; inValues = true; } else if ((inValues || inValValues) && token === ')') { newTokens.push(token); inValValues = false; inValues = false; inClause = false; } else if (modValue && !opsClause && !keywords.has(token.toUpperCase())) { if (token.length > 0) { const nwToken = token.replaceAll(`"`, `'`); newTokens.push(nwToken); modValue = false; } } else { newTokens.push(token); } } const ns = newTokens.join(' '); return ns; } }; UtilsSQL92Compatibility.UtilsSQL92Compatibility = UtilsSQL92Compatibility$1; return UtilsSQL92Compatibility; } var utilsDelete = {}; var hasRequiredUtilsDelete; function requireUtilsDelete () { if (hasRequiredUtilsDelete) return utilsDelete; hasRequiredUtilsDelete = 1; Object.defineProperty(utilsDelete, "__esModule", { value: true }); utilsDelete.UtilsDelete = undefined; class UtilsDeleteError { constructor(message) { this.message = message; } static upDateWhereForDefault(message) { return new UtilsDeleteError(message); } static upDateWhereForRestrict(message) { return new UtilsDeleteError(message); } static upDateWhereForCascade(message) { return new UtilsDeleteError(message); } static executeUpdateForDelete(message) { return new UtilsDeleteError(message); } } class UtilsDelete { getReferencedTableName(refValue) { let tableName = ''; if (refValue.length > 0) { const arr = refValue.split(new RegExp('REFERENCES', 'i')); if (arr.length === 2) { const oPar = arr[1].indexOf('('); tableName = arr[1].substring(0, oPar).trim(); } } return tableName; } upDateWhereForDefault(withRefsNames, results) { let setStmt = ''; let uWhereStmt = ''; try { const key = results.key; const cols = []; for (const relItem of results.relatedItems) { const mVal = relItem[key]; if (mVal !== undefined) { cols.push(mVal); } } // Create the set statement for (const name of withRefsNames) { setStmt += `${name} = NULL, `; } setStmt += 'sql_deleted = 0'; // Create the where statement uWhereStmt = `WHERE ${key} IN (`; for (const col of cols) { uWhereStmt += `${col},`; } if (uWhereStmt.endsWith(',')) { uWhereStmt = uWhereStmt.slice(0, -1); } uWhereStmt += ');'; } catch (error) { const msg = error.message ? error.message : error; throw UtilsDeleteError.upDateWhereForDefault(msg); } return { setStmt, uWhereStmt }; } upDateWhereForRestrict(results) { try { const setStmt = ''; const uWhereStmt = ''; if (results.relatedItems.length > 0) { const msg = 'Restrict mode related items exist, please delete them first'; throw UtilsDeleteError.upDateWhereForRestrict(msg); } return { setStmt, uWhereStmt }; } catch (error) { const msg = error.message ? error.message : error; throw UtilsDeleteError.upDateWhereForRestrict(msg); } } upDateWhereForCascade(results) { let setStmt = ''; let uWhereStmt = ''; try { const key = results.key; const cols = []; for (const relItem of results.relatedItems) { const mVal = relItem[key]; if (mVal !== undefined) { cols.push(mVal); } } setStmt += 'sql_deleted = 1'; // Create the where statement uWhereStmt = `WHERE ${key} IN (`; for (const col of cols) { uWhereStmt += `${col},`; } if (uWhereStmt.endsWith(',')) { uWhereStmt = uWhereStmt.slice(0, -1); } uWhereStmt += ');'; } catch (error) { const msg = error.message ? error.message : error; throw UtilsDeleteError.upDateWhereForCascade(msg); } return { setStmt, uWhereStmt }; } getCurrentTimeAsInteger() { const currentTime = Math.floor(Date.now() / 1000); return currentTime; } checkValuesMatch(array1, array2) { for (const value of array1) { if (!array2.includes(value)) { return false; } } return true; } } utilsDelete.UtilsDelete = UtilsDelete; return utilsDelete; } var utilsFile = {}; var hasRequiredUtilsFile; function requireUtilsFile () { if (hasRequiredUtilsFile) return utilsFile; hasRequiredUtilsFile = 1; Object.defineProperty(utilsFile, "__esModule", { value: true }); utilsFile.UtilsFile = undefined; const promises_1 = require$$0; class UtilsFile { constructor() { this.pathDB = 'Databases'; this.Path = null; this.NodeFs = null; this.NodeFetch = null; this.JSZip = null; this.Os = null; this.Electron = null; this.AppName = ''; this.HomeDir = ''; this.sep = '/'; this.isEncryption = false; this.Path = require$$1; this.NodeFs = require$$2; this.NodeFetch = require$$3; this.Os = require$$4; this.JSZip = require$$5; this.Electron = require$$6; this.HomeDir = this.Os.homedir(); const dir = __dirname; const idx = dir.indexOf('\\'); if (idx != -1) this.sep = '\\'; this.appPath = this.Electron.app.getAppPath(); const rawdata = this.NodeFs.readFileSync(this.Path.resolve(this.appPath, 'package.json')); this.AppName = JSON.parse(rawdata).name; const pathToBuild = this.Path.join(this.appPath, 'build'); if (this.NodeFs.existsSync(this.Path.join(pathToBuild, 'capacitor.config.js'))) { // eslint-disable-next-line @typescript-eslint/no-var-requires this.capConfig = require(this.Path.join(pathToBuild, 'capacitor.config.js')).default; } else { this.capConfig = JSON.parse(this.NodeFs.readFileSync(this.Path.join(this.appPath, 'capacitor.config.json')).toString()); } this.isEncryption = this.capConfig.plugins.CapacitorSQLite.electronIsEncryption ? this.capConfig.plugins.CapacitorSQLite.electronIsEncryption : false; this.osType = this.Os.type(); switch (this.osType) { case 'Darwin': this.pathDB = this.capConfig.plugins.CapacitorSQLite.electronMacLocation ? this.capConfig.plugins.CapacitorSQLite.electronMacLocation : 'Databases'; break; case 'Linux': this.pathDB = this.capConfig.plugins.CapacitorSQLite.electronLinuxLocation ? this.capConfig.plugins.CapacitorSQLite.electronLinuxLocation : 'Databases'; break; case 'Windows_NT': this.pathDB = this.capConfig.plugins.CapacitorSQLite.electronWindowsLocation ? this.capConfig.plugins.CapacitorSQLite.electronWindowsLocation : 'Databases'; break; default: console.log('other operating system'); } } /** * Get isEncryption from config * @returns */ getIsEncryption() { return this.isEncryption; } /** * GetExtName * @param filePath * @returns */ getExtName(filePath) { const matches = filePath.match(/\.([a-zA-Z0-9]+)(?:[\\?#]|$)/); return matches ? `.${matches[1].toLowerCase()}` : ''; // returns the matched extension in lowercase // return this.Path.extname(filePath); } getBaseName(filePath) { const decodedUrl = decodeURIComponent(filePath); // Decode the URL component const baseName = this.Path.basename(decodedUrl, this.Path.extname(filePath)); return baseName; } /** * IsPathExists * @param filePath */ isPathExists(filePath) { let ret = false; try { if (this.NodeFs.existsSync(filePath)) { ret = true; } } catch (err) { console.error('Error isFileExist: ' + err); ret = false; } return ret; } /** * IsFileExists * @param fileName */ isFileExists(fileName) { let ret = false; const filePath = this.getFilePath(fileName); if (filePath.length > 0) { ret = this.isPathExists(filePath); } return ret; } /** * GetFilePath * get the file path * @param fileName */ getFilePath(fileName) { return this.Path.join(this.getDatabasesPath(), fileName); } /** * GetDatabasesPath * get the database folder path */ getDatabasesPath() { let retPath = ''; const sep = this.Path.sep; const dbFolder = this.pathDB; if (dbFolder.includes(sep)) { retPath = dbFolder; if (this.Path.basename(dbFolder) !== this.AppName) { retPath = this.Path.join(dbFolder, this.AppName); } } else { retPath = this.Path.join(this.HomeDir, dbFolder, this.AppName); } const retB = this._createFolderIfNotExists(retPath); if (!retB) retPath = ''; return retPath; } /** * GetCachePath * get the database cache folder path */ getCachePath() { let retPath = ''; const databasePath = this.getDatabasesPath(); retPath = this.Path.join(databasePath, 'cache'); const retB = this._createFolderIfNotExists(retPath); if (!retB) retPath = ''; return retPath; } /** * GetAssetsDatabasesPath * get the assets databases folder path */ getAssetsDatabasesPath() { let retPath = ''; const webDir = this.capConfig.webDir; const dir = webDir === 'www' ? 'src' : 'public'; let mAppPath = this.appPath; if (this.Path.basename(this.appPath) === 'electron') { mAppPath = this.Path.dirname(this.appPath); } retPath = this.Path.resolve(mAppPath, dir, 'assets', 'databases'); return retPath; } /** * SetPathSuffix * @param db */ setPathSuffix(db) { let toDb = db; const ext = '.db'; const dirName = this.Path.dirname(db); const baseName = this.getBaseName(db); if (this.getExtName(db) === ext) { if (!baseName.includes('SQLite')) { const dbName = `${baseName}SQLite`; toDb = `${this.Path.join(dirName, dbName)}${ext}`; } } return toDb; } /** * GetFileList * get the file list for a given folder * @param path */ async getFileList(path) { const filenames = this.NodeFs.readdirSync(path); const dbs = []; filenames.forEach((file) => { if (this.getExtName(file) == '.db' || this.getExtName(file) == '.zip') dbs.push(file); }); return Promise.resolve(dbs); } /** * CopyFromAssetToDatabase * @param db * @param overwrite */ async copyFromAssetToDatabase(db, overwrite) { const pAsset = this.Path.join(this.getAssetsDatabasesPath(), db); const toDb = this.setPathSuffix(db); const pDb = this.Path.join(this.getDatabasesPath(), toDb); await this.copyFilePath(pAsset, pDb, overwrite); return Promise.resolve(); } /** * unzipDatabase * @param db * @param overwrite */ async unzipDatabase(db, fPath, overwrite) { const pZip = this.Path.join(fPath, db); try { // Read the Zip file const data = await this.NodeFs.promises.readFile(pZip); const zip = new this.JSZip(); const contents = await zip.loadAsync(data); // Create an array to store promises for writing files const writePromises = []; Object.keys(contents.files).forEach((filename) => { writePromises.push(zip .file(filename) .async('nodebuffer') .then(async (content) => { const toDb = this.setPathSuffix(filename); const pDb = this.Path.join(this.getDatabasesPath(), toDb); // check filePath exists const isPath = this.isPathExists(pDb); if (!isPath || overwrite) { if (overwrite && isPath) { await this.deleteFilePath(pDb); } await this.NodeFs.promises.writeFile(pDb, content); } })); }); // Wait for all write promises to resolve await Promise.all(writePromises); return Promise.resolve(); } catch (err) { console.log(err); return Promise.reject(`unzipDatabase ${JSON.stringify(err)}`); } } /** * CopyFileName * Copy file name * @param fileName * @param toFileName */ async copyFileName(fileName, toFileName) { // get File Paths const filePath = this.getFilePath(fileName); const toFilePath = this.getFilePath(toFileName); if (filePath.length !== 0 && toFilePath.length !== 0) { try { await this.copyFilePath(filePath, toFilePath, true); return Promise.resolve(); } catch (err) { return Promise.reject(`CopyFileName: ${err}`); } } else { return Promise.reject('CopyFileName: cannot get the ' + 'filePath'); } } /** * CopyFilePath * Copy file Path * @param filePath * @param toFilePath */ async copyFilePath(filePath, toFilePath, overwrite) { if (filePath.length !== 0 && toFilePath.length !== 0) { // check filePath exists const isPath = this.isPathExists(toFilePath); if (!isPath || overwrite) { try { if (overwrite && isPath) { await this.deleteFilePath(toFilePath); } this.NodeFs.copyFileSync(filePath, toFilePath); } catch (err) { return Promise.reject(`CopyFilePath: ${err}`); } } return Promise.resolve(); } else { return Promise.reject('CopyFilePath: cannot get the ' + 'filePath'); } } async copyFile(fromPath, fromFile, toPath, toFile) { const fPath = this.Path.join(fromPath, fromFile); const tPath = this.Path.join(toPath, toFile); try { this.NodeFs.copyFileSync(fPath, tPath); return Promise.resolve(); } catch (err) { return Promise.reject(`CopyFile: ${err}`); } } /** * DeleteFileName * Delete a file by its name * @param fileName */ async deleteFileName(fileName) { // get file path const filePath = this.getFilePath(fileName); if (filePath.length !== 0) { try { await this.deleteFilePath(filePath); return Promise.resolve(); } catch (err) { return Promise.reject('DeleteFileName: delete filePath ' + `failed ${err}`); } } else { return Promise.reject('DeleteFileName: get filePath ' + 'failed'); } } /** * DeleteFilePath * Delete a file by its path * @param filePath */ async deleteFilePath(filePath) { let unlinkRetries = 50000; /** * On windows, the file lock behaves unpredictable. Often it claims a databsae file is locked / busy, although * the file stream is already closed. * Even though we already checked the status with the `waitForFilePathLock()` method previously. * * The only way to handle this reliably is to retry deletion until it works. */ const deleteFile = async () => { try { await promises_1.unlink(filePath); } catch (err) { unlinkRetries--; if (unlinkRetries > 0) { await deleteFile(); } else { throw err; } } }; if (filePath.length !== 0) { // check if path exists const isPath = this.isPathExists(filePath); if (isPath) { try { await this.waitForFilePathLock(filePath); // actually delete the file await deleteFile(); return Promise.resolve(); } catch (err) { return Promise.reject(`DeleteFilePath: ${err}`); } } else { return Promise.resolve(); } } else { return Promise.reject('DeleteFilePath: delete filePath' + 'failed'); } } async waitForFilePathLock(filePath, timeoutMS = 4000) { let timeIsOver = false; setTimeout(() => { timeIsOver = true; }, timeoutMS); return new Promise((resolve, reject) => { const check = async () => { if (timeIsOver) { reject(new Error(`WaitForFilePathLock: The resource is still locked / busy after ${timeoutMS} milliseconds.`)); return; } // check if path exists const isPath = this.isPathExists(filePath); // The file path does not exist. A non existant path cannot be locked. if (!isPath) { resolve(); return; } try { const stream = await promises_1.open(filePath, 'r+'); // We need to close the stream afterwards, because otherwise, we're locking the file await stream.close(); resolve(); } catch (err) { if (err.code === 'EBUSY') { // The resource is busy. Retry in 100ms setTimeout(() => { check(); }, 100); return; } else if (err.code === 'ENOENT') { // The file does not exist (anymore). So it cannot be locked. resolve(); return; } else { // Something else went wrong. reject(new Error(`WaitForFilePathLock: Error while checking the file: ${err}`)); } } }; check(); }); } /** * RenameFileName * @param fileName * @param toFileName */ async renameFileName(fileName, toFileName) { // get File Paths const filePath = this.getFilePath(fileName); const toFilePath = this.getFilePath(toFileName); if (filePath.length !== 0 && toFilePath.length !== 0) { try { await this.renameFilePath(filePath, toFilePath); return Promise.resolve(); } catch (err) { return Promise.reject(`RenameFileName: ${err}`); } } else { return Promise.reject('RenameFileName: filePaths do not ' + 'exist'); } } /** * RenameFilePath * @param filePath * @param toFilePath */ async renameFilePath(filePath, toFilePath) { if (filePath.length !== 0 && toFilePath.length !== 0) { // check filePath exists const isPath = this.isPathExists(filePath); if (isPath) { // delete toFilePath if exists try { await this.deleteFilePath(toFilePath); this.NodeFs.renameSync(filePath, toFilePath); return Promise.resolve(); } catch (err) { return Promise.reject('RenameFilePath: ' + `${err}`); } } else { return Promise.reject(`RenameFilePath: ${filePath} does not exist`); } } else { return Promise.reject('RenameFilePath: filePath not found'); } } async moveDatabaseFromCache() { const cachePath = this.getCachePath(); const databasePath = this.getDatabasesPath(); const dbCacheList = await this.getFileList(cachePath); for (const name of dbCacheList) { const ext = this.getExtName(name); const fromDBName = this.Path.join(cachePath, name); if (ext === '.db') { const pDb = this.setPathSuffix(this.Path.join(databasePath, name)); try { await this.renameFilePath(fromDBName, pDb); } catch (err) { return Promise.reject('moveDatabaseFromCache: ' + `${err}`); } } if (ext === '.zip') { try { await this.deleteFilePath(fromDBName); } catch (err) { return Promise.reject('moveDatabaseFromCache: ' + `${err}`); } } } return Promise.resolve(); } /** * RestoreFileName * @param fileName * @param prefix */ async restoreFileName(fileName, prefix) { const mFileName = `${prefix}-${fileName}`; // check if file exists const isFilePre = this.isFileExists(mFileName); if (isFilePre) { const isFile = this.isFileExists(fileName); if (isFile) { try { await this.deleteFileName(fileName); await this.renameFileName(mFileName, fileName); return Promise.resolve(); } catch (err) { return Promise.reject('RestoreFileName: ' + `${err}`); } } else { return Promise.reject(`RestoreFileName: ${fileName} ` + 'does not exist'); } } else { return Promise.reject(`RestoreFileName: ${mFileName} ` + 'does not exist'); } } /** * DownloadFileFromHTTP * @param url * @param path */ async downloadFileFromHTTP(url, pathFolder) { const res = await this.NodeFetch(url); const ext = this.getExtName(url); const dbName = this.getBaseName(url); const filePath = `${this.Path.join(pathFolder, dbName)}${ext}`; const fileStream = this.NodeFs.createWriteStream(filePath); await new Promise((resolve, reject) => { res.body.pipe(fileStream); res.body.on('error', reject); fileStream.on('finish', resolve); }); } readFileAsPromise(path, options) { return new Promise((resolve, reject) => { const fileStream = this.NodeFs.createReadStream(path, options); const chunks = []; fileStream.on('data', (data) => { chunks.push(data); }); fileStream.on('close', () => { resolve(chunks.toString()); }); fileStream.on('error', (err) => { const msg = err.message ? err.message : err; reject(msg); }); }); } /** * CreateFolderIfNotExists * Create directory * @param folder */ _createFolderIfNotExists(folder) { let ret; try { if (!this.NodeFs.existsSync(folder)) { this._mkdirSyncRecursive(folder); } ret = true; } catch (e) { console.log('Error: in getDBPath', e); ret = false; } return ret; } /** * MkdirSyncRecursive * Create directories recursively * @param directory */ _mkdirSyncRecursive(directory) { const sep = this.Path.sep; const path = directory.replace(/\/$/, '').split(sep); for (let i = 1; i <= path.length; i++) { const segment = path.slice(0, i).join(sep); segment.length > 0 && !this.NodeFs.existsSync(segment) ? this.NodeFs.mkdirSync(segment) : null; } return; } } utilsFile.UtilsFile = UtilsFile; return utilsFile; } var utilsSqlstatement = {}; var hasRequiredUtilsSqlstatement; function requireUtilsSqlstatement () { if (hasRequiredUtilsSqlstatement) return utilsSqlstatement; hasRequiredUtilsSqlstatement = 1; Object.defineProperty(utilsSqlstatement, "__esModule", { value: true }); utilsSqlstatement.UtilsSQLStatement = undefined; class UtilsSQLStatement { constructor() { this.replaceString = (originalStr, searchStr, replaceStr) => { const range = originalStr.indexOf(searchStr); if (range !== -1) { const modifiedStr = originalStr.substring(0, range) + replaceStr + originalStr.substring(range + searchStr.length); return modifiedStr; } return originalStr; }; } extractTableName(statement) { const pattern = /(?:INSERT\s+INTO|UPDATE|DELETE\s+FROM)\s+([^\s]+)/i; const match = statement.match(pattern); if (match?.[1]) { const tableName = match[1]; return tableName; } return null; } extractWhereClause(statement) { const pattern = /WHERE(.+?)(?:ORDER\s+BY|LIMIT|$)/i; const match = statement.match(pattern); if (match?.[1]) { const whereClause = match[1].trim(); return whereClause; } return null; } addPrefixToWhereClause(whereClause, colNames, refNames, prefix) { let columnValuePairs; if (whereClause.includes('AND')) { // Split the WHERE clause based on the "AND" keyword const subSequenceArray = whereClause.split('AND'); columnValuePairs = subSequenceArray.map((pair) => pair.trim()); } else { columnValuePairs = [whereClause]; } const modifiedPairs = columnValuePairs.map((pair) => { const match = pair.match(/(\w+)\s*(=|IN|BETWEEN|LIKE)\s*(.+)/); if (!match) { return pair; } const column = match[1].trim(); const operator = match[2].trim(); const value = match[3].trim(); let newColumn = column; const index = this.findIndexOfStringInArray(column, refNames); if (index !== -1) { newColumn = this.getStringAtIndex(colNames, index); } const modifiedColumn = `${prefix}${newColumn}`; const ret = `${modifiedColumn} ${operator} ${value}`; return ret; }); return modifiedPairs.join(' AND '); } findIndexOfStringInArray(target, array) { return array.indexOf(target); } getStringAtIndex(array, index) { if (index >= 0 && index < array.length) { return array[index]; } else { return undefined; } } extractForeignKeyInfo(sqlStatement) { // Define the regular expression pattern for extracting the FOREIGN KEY clause const foreignKeyPattern = /\bFOREIGN\s+KEY\s*\(([^)]+)\)\s+REFERENCES\s+(\w+)\s*\(([^)]+)\)\s+(ON\s+DELETE\s+(RESTRICT|CASCADE|SET\s+NULL|SET\s+DEFAULT|NO\s+ACTION))?/; const matches = sqlStatement.match(foreignKeyPattern); if (matches) { const foreignKeyInfo = { forKeys: matches[1].split(',').map((key) => key.trim()), tableName: matches[2], refKeys: matches[3].split(',').map((key) => key.trim()), action: matches[5] ? matches[5] : 'NO ACTION', }; return foreignKeyInfo; } else { throw new Error('extractForeignKeyInfo: No FOREIGN KEY found'); } } extractColumnNames(whereClause) { const keywords = new Set(['AND', 'OR', 'IN', 'VALUES', 'LIKE', 'BETWEEN', 'NOT']); const regex = /\b[a-zA-Z]\w*\b(?=\s*(?:<=?|>=?|<>?|=|AND|OR|BETWEEN|NOT|IN|LIKE))|\b[a-zA-Z]\w*\b\s+BETWEEN\s+'[^']+'\s+AND\s+'[^']+'|\(([^)]+)\)\s+IN\s+\(?\s*VALUES\s*\(/g; let match; const columns = []; while ((match = regex.exec(whereClause)) !== null) { const columnList = match[1]; if (columnList) { const columnNamesArray = columnList.split(','); for (const columnName of columnNamesArray) { columns.push(columnName.trim()); } } else { const matchedText = match[0]; if (!keywords.has(matchedText.trim().toUpperCase())) { columns.push(matchedText.trim()); } } } return columns; } flattenMultilineString(input) { const lines = input.split(/\r?\n/); return lines.join(' '); } extractCombinedPrimaryKey(whereClause) { const pattern = /WHERE\s*\((.+?)\)\s*(?:=|IN)\s*\((.+?)\)/g; const regex = new RegExp(pattern); const matches = whereClause.matchAll(regex); const primaryKeySets = []; for (const match of matches) { const keysString = match[1].trim(); const keys = keysString.split(',').map((key) => key.trim()); primaryKeySets.push(keys); } return primaryKeySets.length === 0 ? null : primaryKeySets; } getWhereStmtForCombinedPK(whStmt, withRefs, colNames, keys) { let retWhere = whStmt; for (const grpKeys of keys) { const repKeys = grpKeys.join(',') === withRefs.join(',') ? colNames : withRefs; for (const [index, key] of grpKeys.entries()) { retWhere = this.replaceAllString(retWhere, key, repKeys[index]); } } return retWhere; } replaceAllString(originalStr, searchStr, replaceStr) { return originalStr.split(searchStr).join(replaceStr); } indicesOf(str, searchStr, fromIndex = 0) { // Helper function to find indices of a substring within a string const indices = []; let currentIndex = str.indexOf(searchStr, fromIndex); while (currentIndex !== -1) { indices.push(currentIndex); currentIndex = str.indexOf(searchStr, currentIndex + 1); } return indices; } getWhereStmtForNonCombinedPK(whStmt, withRefs, colNames) { let whereStmt = ''; let stmt = whStmt.substring(6); for (let idx = 0; idx < withRefs.length; idx++) { let colType = 'withRefsNames'; let idxs = this.indicesOf(stmt, withRefs[idx]); if (idxs.length === 0) { idxs = this.indicesOf(stmt, colNames[idx]); colType = 'colNames'; } if (idxs.length > 0) { let valStr = ''; const indicesEqual = this.indicesOf(stmt, '=', idxs[0]); if (indicesEqual.length > 0) { const indicesAnd = this.indicesOf(stmt, 'AND', indicesEqual[0]); if (indicesAnd.length > 0) { valStr = stmt.substring(indicesEqual[0] + 1, indicesAnd[0] - 1); stmt = stmt.substring(indicesAnd[0] + 3); } else { valStr = stmt.substring(indicesEqual[0] + 1); } if (idx > 0) { whereStmt += ' AND '; } if (colType === 'withRefsNames') { whereStmt += colNames[idx] + ' = ' + valStr; } else { whereStmt += withRefs[idx] + ' = ' + valStr; } } } } whereStmt = 'WHERE ' + whereStmt; return whereStmt; } updateWhere(whStmt, withRefs, colNames) { let whereStmt = ''; if (whStmt.length <= 0) { return whereStmt; } if (whStmt.toUpperCase().substring(0, 5) !== 'WHERE') { return whereStmt; } if (withRefs.length === colNames.length) { // get whereStmt for primary combined key const keys = this.extractCombinedPrimaryKey(whStmt); if (keys) { whereStmt = this.getWhereStmtForCombinedPK(whStmt, withRefs, colNames, keys); } else { // get for non primary combined key whereStmt = this.getWhereStmtForNonCombinedPK(whStmt, withRefs, colNames); } } return whereStmt; } } utilsSqlstatement.UtilsSQLStatement = UtilsSQLStatement; return utilsSqlstatement; } var hasRequiredUtilsSQLite; function requireUtilsSQLite () { if (hasRequiredUtilsSQLite) return utilsSQLite; hasRequiredUtilsSQLite = 1; Object.defineProperty(utilsSQLite, "__esModule", { value: true }); utilsSQLite.UtilsSQLite = undefined; const UtilsSQL92Compatibility_1 = requireUtilsSQL92Compatibility(); const utilsDelete_1 = requireUtilsDelete(); const utilsFile_1 = requireUtilsFile(); const utilsSqlstatement_1 = requireUtilsSqlstatement(); //const SQLITE_OPEN_READONLY = 1; class UtilsSQLite { constructor() { this.fileUtil = new utilsFile_1.UtilsFile(); this.statUtil = new utilsSqlstatement_1.UtilsSQLStatement(); this.delUtil = new utilsDelete_1.UtilsDelete(); this.sql92Utils = new UtilsSQL92Compatibility_1.UtilsSQL92Compatibility(); this.BCSQLite3 = require$$4$1; } /** * OpenOrCreateDatabase * @param pathDB * @param password */ openOrCreateDatabase(pathDB, password, readonly) { const msg = 'OpenOrCreateDatabase'; // open sqlite3 database let mDB; if (!readonly) { mDB = new this.BCSQLite3(pathDB, { // verbose: console.log, fileMustExist: false, }); } else { mDB = new this.BCSQLite3(pathDB, { // verbose: console.log, readonly: true, fileMustExist: true, }); } if (mDB != null) { try { this.dbChanges(mDB); } catch (err) { const errmsg = err.message ? err.message : err; throw new Error(`${msg} ${errmsg}`); } try { // set the password if (password.length > 0) { this.setCipherPragma(mDB, password); } // set Foreign Keys On this.setForeignKeyConstraintsEnabled(mDB, true); } catch (err) { const errmsg = err.message ? err.message : err; throw new Error(`${msg} ${errmsg}`); } return mDB; } else { throw new Error(msg + 'open database failed'); } } /** * SetCipherPragma * @param mDB * @param password */ setCipherPragma(mDB, passphrase) { const msg = 'setCipherPragma'; try { mDB.pragma(`cipher='sqlcipher'`); mDB.pragma(`legacy=4`); mDB.pragma(`key='${passphrase}'`); return; } catch (err) { const errmsg = err.message ? err.message : err; throw new Error(`${msg} ${errmsg}`); } } /** * SetForeignKeyConstraintsEnabled * @param mDB * @param toggle */ setForeignKeyConstraintsEnabled(mDB, toggle) { const msg = 'SetForeignKeyConstraintsEnabled'; let key = 'OFF'; if (toggle) { key = 'ON'; } try { mDB.pragma(`foreign_keys = '${key}'`); return; } catch (err) { const errmsg = err.message ? err.message : err; throw new Error(`${msg} ${errmsg}`); } } /** * CloseDB * @param mDB */ closeDB(mDB) { const msg = 'closeDB'; try { mDB.close(); return; } catch (err) { const errmsg = err.message ? err.message : err; throw new Error(`${msg} ${errmsg}`); } } /** * GetVersion * @param mDB */ getVersion(mDB) { const msg = 'GetVersion'; try { const result = mDB.pragma('user_version'); return result[0].user_version; } catch (err) { const errmsg = err.message ? err.message : err; throw new Error(`${msg} ${errmsg}`); } } /** * SetVersion * @param mDB * @param version */ setVersion(mDB, version) { const msg = 'SetVersion'; try { mDB.pragma(`user_version = '${version}'`); return; } catch (err) { const errmsg = err.message ? err.message : err; throw new Error(`${msg} ${errmsg}`); } } /** * ChangePassword * @param pathDB * @param password * @param newpassword */ changePassword(pathDB, password, newpassword) { let mDB; const msg = 'ChangePassword'; try { mDB = this.openOrCreateDatabase(pathDB, password, false); this.pragmaReKey(mDB, password, newpassword); } catch (err) { const errmsg = err.message ? err.message : err; throw new Error(`${msg} ${errmsg}`); } finally { this.closeDB(mDB); } return; } /** * PragmaReKey * @param mDB * @param passphrase * @param newpassphrase */ pragmaReKey(mDB, passphrase, newpassphrase) { const msg = 'PragmaReKey: '; try { mDB.pragma(`cipher='sqlcipher'`); mDB.pragma(`legacy=4`); mDB.pragma(`key='${passphrase}'`); mDB.pragma(`rekey='${newpassphrase}'`); return; } catch (err) { const errmsg = err.message ? err.message : err; throw new Error(`${msg} ${errmsg}`); } } /** * BeginTransaction * @param db * @param isOpen */ beginTransaction(db, isOpen) { // eslint-disable-next-line no-async-promise-executor const msg = 'BeginTransaction: '; if (!isOpen) { throw new Error(`${msg} database not opened`); } const sql = 'BEGIN TRANSACTION;'; try { db.exec(sql); return; } catch (err) { const errmsg = err.message ? err.message : err; throw new Error(`${msg} ${errmsg}`); } } /** * RollbackTransaction * @param db * @param isOpen */ rollbackTransaction(db, isOpen) { const msg = 'RollbackTransaction: '; if (!isOpen) { throw new Error(`${msg} database not opened`); } const sql = 'ROLLBACK TRANSACTION;';