import {StorageEngine} from './storage'; import * as util from 'ionic/util'; const DB_NAME = '__ionicstorage'; /** * SqlStorage uses SQLite or WebSQL (development only!) to store data in a * persistent SQL store on the filesystem. * * This is the preferred storage engine, as data will be stored in appropriate * app storage, unlike Local Storage which is treated differently by the OS. */ export class SqlStorage extends StorageEngine { static BACKUP_LOCAL = 2 static BACKUP_LIBRARY = 1 static BACKUP_DOCUMENTS = 0 constructor(options) { super(); let dbOptions = util.defaults({ name: DB_NAME, backupFlag: SqlStorage.BACKUP_NONE, existingDatabase: false }, options); if(window.sqlitePlugin) { let location = this._getBackupLocation(dbOptions); this._db = window.sqlitePlugin.openDatabase(util.extend({ name: dbOptions.name, location: location, createFromLocation: dbOptions.existingDatabase ? 1 : 0 }, dbOptions)); } else { console.warn('Storage: SQLite plugin not installed, falling back to WebSQL. Make sure to install cordova-sqlite-storage in production!'); this._db = window.openDatabase(dbOptions.name, '1.0', 'database', 5 * 1024 * 1024); } this._tryInit(); } _getBackupLocation(dbFlag) { switch(dbFlag) { case SqlStorage.BACKUP_LOCAL: return 2; case SqlStorage.BACKUP_LIBRARY: return 1; case SqlStorage.BACKUP_DOCUMENTS: return 0; default: throw Error('Invalid backup flag: ' + dbFlag); } } // Initialize the DB with our required tables _tryInit() { this._db.transaction((tx) => { tx.executeSql('CREATE TABLE IF NOT EXISTS kv (key text primary key, value text)', [], (tx, res) => { }, (tx, err) => { console.error('Storage: Unable to create initial storage tables', tx, err); }); }); } /** * Perform an arbitrary SQL operation on the database. Use this method * to have full control over the underlying database through SQL operations * like SELECT, INSERT, and UPDATE. * * @param {string} query the query to run * @return {Promise} that resolves or rejects with an object of the form { tx: Transaction, res: Result (or err)} */ query(query) { return new Promise((resolve, reject) => { this._db.transaction((tx) => { ts.executeSql(query, [], (tx, res) => { resolve({ tx: tx, res: res }); }, (tx, err) => { reject({ tx: tx, err: err }); }) }); }) } /** * Get the value in the database identified by the given key. * @param {string} key the key * @return {Promise} that resolves or rejects with an object of the form { tx: Transaction, res: Result (or err)} */ get(key) { return new Promise((resolve, reject) => { try { this._db.transaction(tx => { tx.executeSql("select key, value from kv where key = ? limit 1", [key], (tx, res) => { if(res.rows.length > 0) { let item = res.rows.item(0); resolve(item.value); } resolve(null); }, (tx, err) => { reject({ tx: tx, err: err }); }) }, err => { reject(err); }); } catch(e) { reject(e); } }); } /** * Set the value in the database for the given key. Existing values will be overwritten. * @param {string} key the key * @param {string} value The value (as a string) * @return {Promise} that resolves or rejects with an object of the form { tx: Transaction, res: Result (or err)} */ set(key, value) { return new Promise((resolve, reject) => { try { this._db.transaction(tx => { tx.executeSql('insert or replace into kv(key, value) values (?, ?)', [key, value], (tx, res) => { resolve(); }, (tx, err) => { reject({ tx: tx, err: err }); }) }, err => { reject(err); }); } catch(e) { reject(e); } }); } /** * Remove the value in the database for the given key. * @param {string} key the key * @param {string} value The value (as a string) * @return {Promise} that resolves or rejects with an object of the form { tx: Transaction, res: Result (or err)} */ remove(key) { return new Promise((resolve, reject) => { try { this._db.transaction(tx => { tx.executeSql('delete from kv where key = ?', [key], (tx, res) => { resolve(); }, (tx, err) => { reject({ tx: tx, err: err }); }) }, err => { reject(err); }); } catch(e) { reject(e); } }); } }