/** * @private */ Ext.define('Ext.device.sqlite.Sencha', { /** * Returns a {@link Ext.device.sqlite.Database} instance. * If the database with specified name does not exist, it will be created. * If the creationCallback is provided, * the database is created with the empty string as its version regardless of the specified version. * * @param {Object} config * The object which contains the following config options: * * @param {String} config.name This is required. * The name of the database to open. * * @param {String} config.version This is required. * The version of the database to open. * * @param {String} config.displayName This is required. * The display name of the database to open. * * @param {Number} config.estimatedSize This is required. * The estimated size of the database to open. * * @param {Function} config.creationCallback This is optional. * The callback to be called when the database has been created. * * @param {Ext.device.sqlite.Database} config.creationCallback.database * The created database with the empty string as its version regardless of the specified version. * * @param {Object} config.scope This is optional. * The scope object. * * @return {Ext.device.sqlite.Database} * The opened database, null if an error occured. */ openDatabase: function(config) { if (config.name == null) { Ext.Logger.error('Ext.device.SQLite#openDatabase: You must specify a `name` of the database.'); return null; } if (config.version == null) { Ext.Logger.error('Ext.device.SQLite#openDatabase: You must specify a `version` of the database.'); return null; } if (config.displayName == null) { Ext.Logger.error('Ext.device.SQLite#openDatabase: You must specify a `displayName` of the database.'); return null; } if (config.estimatedSize == null) { Ext.Logger.error('Ext.device.SQLite#openDatabase: You must specify a `estimatedSize` of the database.'); return null; } var database = null; var result = Ext.device.Communicator.send({ command: 'SQLite#openDatabase', sync: true, name: config.name, version: config.version, displayName: config.displayName, estimatedSize: config.estimatedSize, callbacks: { // `creationCallback != null` is checked for internal logic in native plugin code creationCallback: !config.creationCallback ? null : function() { config.creationCallback.call(config.scope || this, database); } }, scope: config.scope || this }); if (result) { if (result.error) { Ext.Logger.error(result.error); return null; } database = Ext.create('Ext.device.sqlite.Database', result.id, result.version); return database; } return null; } }, function() { /** * The Database class which is used to perform transactions. */ Ext.define('Ext.device.sqlite.Database', { id: 0, version: null, constructor: function(id, version) { this.id = id; this.version = version; }, /** * Returns the current version of the database. * * @return {String} * The database current version. */ getVersion: function() { return Ext.device.Communicator.send({ command: 'SQLite#getVersion', sync: true, databaseId: this.id }); }, /** * Performs a {@link Ext.device.sqlite.SQLTransaction} instance in a read/write mode. * * @param {Object} config * The object which contains the following config options: * * @param {Function} config.callback This is required. * The callback to be called when the transaction has been created. * * @param {Ext.device.sqlite.SQLTransaction} config.callback.transaction * The created transaction. * * @param {Function} config.success This is optional. * The callback to be called when the transaction has been successfully commited. * * @param {Function} config.failure This is optional. * The callback to be called when an error occurred and the transaction has been rolled back. * * @param {Object} config.failure.error * The occurred error. * * @param {Object} config.scope * The scope object */ transaction: function(config) { if (!config.callback) { Ext.Logger.error('Ext.device.sqlite.Database#transaction: You must specify a `callback` callback.'); return null; } var me = this; Ext.device.Communicator.send({ command: 'SQLite#createTransaction', databaseId: this.id, readOnly: config.readOnly, callbacks: { success: function(id) { var exception = null; var error = null; var transaction = Ext.create('Ext.device.sqlite.SQLTransaction', id); error = Ext.device.Communicator.send({ command: 'SQLite#beginTransaction', sync: true, transactionId: transaction.id }); if (!error && config.preflight) { error = config.preflight.call(config.scope || this); } if (!error) { try { transaction.active = true; config.callback.call(config.scope || this, transaction); // may throw exception } catch (e) { exception = e; } finally { transaction.active = false; } } var statements = transaction.statements; while (!(exception || error) && statements.length > 0) { var statement = statements.shift(); var result = Ext.device.Communicator.send({ command: 'SQLite#executeStatement', sync: true, transactionId: transaction.id, databaseId: me.id, version: me.version, sqlStatement: statement.sqlStatement, arguments: JSON.stringify(statement.arguments) }); if (result) { if (result.error) { error = result.error; } else if (statement.callback) { var resultSet = Ext.create('Ext.device.sqlite.SQLResultSet', result); try { transaction.active = true; statement.callback.call(statement.scope || this, transaction, resultSet); // may throw exception } catch (e) { exception = e; } finally { transaction.active = false; } } } if (error && statement.failure) { try { transaction.active = true; if (!statement.failure.call(statement.scope || this, transaction, error)) { // may throw exception error = null; } } catch (e) { exception = e; } finally { transaction.active = false; } } } if (!(exception || error)) { error = Ext.device.Communicator.send({ command: 'SQLite#commitTransaction', sync: true, transactionId: transaction.id }); if (!error) { if (config.postflight) { config.postflight.call(config.scope || this); } if (config.success) { config.success.call(config.scope || this); } } } if (exception || error) { statements.splice(0, statements.length); Ext.device.Communicator.send({ command: 'SQLite#rollbackTransaction', sync: true, transactionId: transaction.id }); if (exception) { throw exception; } else if (config.failure) { config.failure.call(config.scope || this, error); } } }, failure: function(error) { if (config.failure) { config.failure.call(config.scope || this, error); } } }, scope: config.scope || this }); }, /** * Works the same way as {@link Ext.device.sqlite.Database#transaction}, * but performs a {@link Ext.device.sqlite.SQLTransaction} instance in a read-only mode. */ readTransaction: function(config) { this.transaction(Ext.apply(config, { readOnly: true })); }, /** * Verifies and changes the version of the database at the same time * as doing a schema update with a {@link Ext.device.sqlite.SQLTransaction} instance. * * @param {Object} config * The object which contains the following config options: * * @param {String} config.oldVersion This is required. * The current version of the database. * * @param {String} config.newVersion This is required. * The new version of the database. * * @param {Function} config.callback This is optional. * The callback to be called when the transaction has been created. * * @param {Ext.device.sqlite.SQLTransaction} config.callback.transaction * The created transaction. * * @param {Function} config.success This is optional. * The callback to be called when the transaction has been successfully commited. * * @param {Function} config.failure This is optional. * The callback to be called when an error occurred and the transaction has been rolled back. * * @param {Object} config.failure.error * The occurred error. * * @param {Object} config.scope * The scope object */ changeVersion: function(config) { if (config.oldVersion == null) { Ext.Logger.error('Ext.device.SQLite#changeVersion: You must specify an `oldVersion` of the database.'); return null; } if (config.newVersion == null) { Ext.Logger.error('Ext.device.SQLite#changeVersion: You must specify a `newVersion` of the database.'); return null; } this.transaction(Ext.apply(config, { preflight: function() { return config.oldVersion == this.getVersion() ? null : 'Unable to change version: version mismatch'; }, postflight: function() { var result = Ext.device.Communicator.send({ command: 'SQLite#setVersion', sync: true, databaseId: this.id, version: config.newVersion }); if (result) { this.version = config.newVersion; } } })); } }, function() { /** * The SQLTransaction class which is used to execute SQL statements. */ Ext.define('Ext.device.sqlite.SQLTransaction', { id: 0, active: false, statements: null, constructor: function(id) { this.id = id; this.statements = new Array(); }, /** * Executes an SQL statement. * * @param {Object} config * The object which contains the following config options: * * @param {String} config.sqlStatement This is required. * The SQL statement to execute. * * @param {Array} config.arguments This is optional. * The arguments array to bind each '?' placeholder in the SQL statement. * * @param {Function} config.callback This is optional. * The callback to be called when the SQL statement succeeded. * * @param {Ext.device.sqlite.SQLTransaction} config.callback.transaction * The transaction of the SQL statement. * * @param {Ext.device.sqlite.SQLTransaction} config.callback.resultSet * The result of the SQL statement. * * @param {Function} config.failure This is optional. * The callback to be called when an error occurred. * If the callback returns false, next SQL statement will be executed. * * @param {Ext.device.sqlite.SQLTransaction} config.failure.transaction * The transaction of the SQL statement. * * @param {Object} config.failure.error * The occurred error. * * @param {Object} config.scope * The scope object */ executeSql: function(config) { if (!this.active) { Ext.Logger.error('Ext.device.sqlite.SQLTransaction#executeSql: An attempt was made to use a SQLTransaction that is no longer usable.'); return null; } if (config.sqlStatement == null) { Ext.Logger.error('Ext.device.sqlite.SQLTransaction#executeSql: You must specify a `sqlStatement` for the transaction.'); return null; } this.statements.push({ sqlStatement: config.sqlStatement, arguments: config.arguments, callback: config.callback, failure: config.failure, scope: config.scope }); } }, function() { /** * The SQLResultSet class which is used to represent SQL statements results. */ Ext.define('Ext.device.sqlite.SQLResultSet', { insertId: 0, rowsAffected: 0, rows: null, constructor: function(data) { this.insertId = data.insertId; this.rowsAffected = data.rowsAffected; this.rows = Ext.create('Ext.device.sqlite.SQLResultSetRowList', data); }, /** * Returns the row ID of the last row that the SQL statement inserted into the database, * if the statement inserted any rows. * If the statement did not insert a row, throws an exception. * * @return {Number} * The inserted row ID. */ getInsertId: function() { if (this.insertId != 0) { return this.insertId; } else { Ext.Logger.error('Ext.device.sqlite.SQLResultSet#getInsertId: An SQLTransaction did not insert a row.'); return null; } }, /** * Returns the number of rows that were changed by the SQL statement. * If the statement did not change any rows, returns zero. * * @return {Number} * The number of rows affected. */ getRowsAffected: function() { return this.rowsAffected; }, /** * Returns a {@link Ext.device.sqlite.SQLResultSetRowList} instance representing rows returned by the SQL statement. * * @return {Ext.device.sqlite.SQLResultSetRowList} * The rows. */ getRows: function() { return this.rows; } }, function() { /** * The SQLResultSetRowList class which is used to represent rows returned by SQL statements. */ Ext.define('Ext.device.sqlite.SQLResultSetRowList', { names: null, rows: null, constructor: function(data) { this.names = data.names; this.rows = data.rows; }, /** * Returns the number of rows returned by the SQL statement. * * @return {Number} * The number of rows. */ getLength: function() { return this.rows.length; }, /** * Returns a row at specified index returned by the SQL statement. * If there is no such row, returns null. * * @param {Number} index This is required. * The index of a row. * * @return {Object} * The row. */ item: function(index) { if (index < this.getLength()) { var item = {}; var row = this.rows[index]; this.names.forEach(function(name, index) { item[name] = row[index]; }); return item; } return null; } }); }); }); }); });