]> git.proxmox.com Git - sencha-touch.git/blob - src/src/device/sqlite/Sencha.js
import Sencha Touch 2.4.2 source
[sencha-touch.git] / src / src / device / sqlite / Sencha.js
1 /**
2 * @private
3 */
4 Ext.define('Ext.device.sqlite.Sencha', {
5 /**
6 * Returns a {@link Ext.device.sqlite.Database} instance.
7 * If the database with specified name does not exist, it will be created.
8 * If the creationCallback is provided,
9 * the database is created with the empty string as its version regardless of the specified version.
10 *
11 * @param {Object} config
12 * The object which contains the following config options:
13 *
14 * @param {String} config.name This is required.
15 * The name of the database to open.
16 *
17 * @param {String} config.version This is required.
18 * The version of the database to open.
19 *
20 * @param {String} config.displayName This is required.
21 * The display name of the database to open.
22 *
23 * @param {Number} config.estimatedSize This is required.
24 * The estimated size of the database to open.
25 *
26 * @param {Function} config.creationCallback This is optional.
27 * The callback to be called when the database has been created.
28 *
29 * @param {Ext.device.sqlite.Database} config.creationCallback.database
30 * The created database with the empty string as its version regardless of the specified version.
31 *
32 * @param {Object} config.scope This is optional.
33 * The scope object.
34 *
35 * @return {Ext.device.sqlite.Database}
36 * The opened database, null if an error occured.
37 */
38 openDatabase: function(config) {
39 if (config.name == null) {
40 Ext.Logger.error('Ext.device.SQLite#openDatabase: You must specify a `name` of the database.');
41 return null;
42 }
43
44 if (config.version == null) {
45 Ext.Logger.error('Ext.device.SQLite#openDatabase: You must specify a `version` of the database.');
46 return null;
47 }
48
49 if (config.displayName == null) {
50 Ext.Logger.error('Ext.device.SQLite#openDatabase: You must specify a `displayName` of the database.');
51 return null;
52 }
53
54 if (config.estimatedSize == null) {
55 Ext.Logger.error('Ext.device.SQLite#openDatabase: You must specify a `estimatedSize` of the database.');
56 return null;
57 }
58
59 var database = null;
60
61 var result = Ext.device.Communicator.send({
62 command: 'SQLite#openDatabase',
63 sync: true,
64 name: config.name,
65 version: config.version,
66 displayName: config.displayName,
67 estimatedSize: config.estimatedSize,
68 callbacks: {
69 // `creationCallback != null` is checked for internal logic in native plugin code
70 creationCallback: !config.creationCallback ? null : function() {
71 config.creationCallback.call(config.scope || this, database);
72 }
73 },
74 scope: config.scope || this
75 });
76
77 if (result) {
78 if (result.error) {
79 Ext.Logger.error(result.error);
80 return null;
81 }
82
83 database = Ext.create('Ext.device.sqlite.Database', result.id, result.version);
84
85 return database;
86 }
87
88 return null;
89 }
90 }, function() {
91 /**
92 * The Database class which is used to perform transactions.
93 */
94 Ext.define('Ext.device.sqlite.Database', {
95 id: 0,
96 version: null,
97
98 constructor: function(id, version) {
99 this.id = id;
100 this.version = version;
101 },
102
103 /**
104 * Returns the current version of the database.
105 *
106 * @return {String}
107 * The database current version.
108 */
109 getVersion: function() {
110 return Ext.device.Communicator.send({
111 command: 'SQLite#getVersion',
112 sync: true,
113 databaseId: this.id
114 });
115 },
116
117 /**
118 * Performs a {@link Ext.device.sqlite.SQLTransaction} instance in a read/write mode.
119 *
120 * @param {Object} config
121 * The object which contains the following config options:
122 *
123 * @param {Function} config.callback This is required.
124 * The callback to be called when the transaction has been created.
125 *
126 * @param {Ext.device.sqlite.SQLTransaction} config.callback.transaction
127 * The created transaction.
128 *
129 * @param {Function} config.success This is optional.
130 * The callback to be called when the transaction has been successfully commited.
131 *
132 * @param {Function} config.failure This is optional.
133 * The callback to be called when an error occurred and the transaction has been rolled back.
134 *
135 * @param {Object} config.failure.error
136 * The occurred error.
137 *
138 * @param {Object} config.scope
139 * The scope object
140 */
141 transaction: function(config) {
142 if (!config.callback) {
143 Ext.Logger.error('Ext.device.sqlite.Database#transaction: You must specify a `callback` callback.');
144 return null;
145 }
146
147 var me = this;
148 Ext.device.Communicator.send({
149 command: 'SQLite#createTransaction',
150 databaseId: this.id,
151 readOnly: config.readOnly,
152 callbacks: {
153 success: function(id) {
154 var exception = null;
155 var error = null;
156 var transaction = Ext.create('Ext.device.sqlite.SQLTransaction', id);
157
158 error = Ext.device.Communicator.send({
159 command: 'SQLite#beginTransaction',
160 sync: true,
161 transactionId: transaction.id
162 });
163
164 if (!error && config.preflight) {
165 error = config.preflight.call(config.scope || this);
166 }
167
168 if (!error) {
169 try {
170 transaction.active = true;
171 config.callback.call(config.scope || this, transaction); // may throw exception
172 } catch (e) {
173 exception = e;
174 } finally {
175 transaction.active = false;
176 }
177 }
178
179 var statements = transaction.statements;
180
181 while (!(exception || error) && statements.length > 0) {
182 var statement = statements.shift();
183 var result = Ext.device.Communicator.send({
184 command: 'SQLite#executeStatement',
185 sync: true,
186 transactionId: transaction.id,
187 databaseId: me.id,
188 version: me.version,
189 sqlStatement: statement.sqlStatement,
190 arguments: JSON.stringify(statement.arguments)
191 });
192
193 if (result) {
194 if (result.error) {
195 error = result.error;
196 } else if (statement.callback) {
197 var resultSet = Ext.create('Ext.device.sqlite.SQLResultSet', result);
198
199 try {
200 transaction.active = true;
201 statement.callback.call(statement.scope || this, transaction, resultSet); // may throw exception
202 } catch (e) {
203 exception = e;
204 } finally {
205 transaction.active = false;
206 }
207 }
208 }
209
210 if (error && statement.failure) {
211 try {
212 transaction.active = true;
213 if (!statement.failure.call(statement.scope || this, transaction, error)) { // may throw exception
214 error = null;
215 }
216 } catch (e) {
217 exception = e;
218 } finally {
219 transaction.active = false;
220 }
221 }
222 }
223
224 if (!(exception || error)) {
225 error = Ext.device.Communicator.send({
226 command: 'SQLite#commitTransaction',
227 sync: true,
228 transactionId: transaction.id
229 });
230
231 if (!error) {
232 if (config.postflight) {
233 config.postflight.call(config.scope || this);
234 }
235
236 if (config.success) {
237 config.success.call(config.scope || this);
238 }
239 }
240 }
241
242 if (exception || error) {
243 statements.splice(0, statements.length);
244
245 Ext.device.Communicator.send({
246 command: 'SQLite#rollbackTransaction',
247 sync: true,
248 transactionId: transaction.id
249 });
250
251 if (exception) {
252 throw exception;
253 } else if (config.failure) {
254 config.failure.call(config.scope || this, error);
255 }
256 }
257 },
258 failure: function(error) {
259 if (config.failure) {
260 config.failure.call(config.scope || this, error);
261 }
262 }
263 },
264 scope: config.scope || this
265 });
266 },
267
268 /**
269 * Works the same way as {@link Ext.device.sqlite.Database#transaction},
270 * but performs a {@link Ext.device.sqlite.SQLTransaction} instance in a read-only mode.
271 */
272 readTransaction: function(config) {
273 this.transaction(Ext.apply(config, {
274 readOnly: true
275 }));
276 },
277
278 /**
279 * Verifies and changes the version of the database at the same time
280 * as doing a schema update with a {@link Ext.device.sqlite.SQLTransaction} instance.
281 *
282 * @param {Object} config
283 * The object which contains the following config options:
284 *
285 * @param {String} config.oldVersion This is required.
286 * The current version of the database.
287 *
288 * @param {String} config.newVersion This is required.
289 * The new version of the database.
290 *
291 * @param {Function} config.callback This is optional.
292 * The callback to be called when the transaction has been created.
293 *
294 * @param {Ext.device.sqlite.SQLTransaction} config.callback.transaction
295 * The created transaction.
296 *
297 * @param {Function} config.success This is optional.
298 * The callback to be called when the transaction has been successfully commited.
299 *
300 * @param {Function} config.failure This is optional.
301 * The callback to be called when an error occurred and the transaction has been rolled back.
302 *
303 * @param {Object} config.failure.error
304 * The occurred error.
305 *
306 * @param {Object} config.scope
307 * The scope object
308 */
309 changeVersion: function(config) {
310 if (config.oldVersion == null) {
311 Ext.Logger.error('Ext.device.SQLite#changeVersion: You must specify an `oldVersion` of the database.');
312 return null;
313 }
314
315 if (config.newVersion == null) {
316 Ext.Logger.error('Ext.device.SQLite#changeVersion: You must specify a `newVersion` of the database.');
317 return null;
318 }
319
320 this.transaction(Ext.apply(config, {
321 preflight: function() {
322 return config.oldVersion == this.getVersion() ? null : 'Unable to change version: version mismatch';
323 },
324 postflight: function() {
325 var result = Ext.device.Communicator.send({
326 command: 'SQLite#setVersion',
327 sync: true,
328 databaseId: this.id,
329 version: config.newVersion
330 });
331
332 if (result) {
333 this.version = config.newVersion;
334 }
335 }
336 }));
337 }
338 }, function() {
339 /**
340 * The SQLTransaction class which is used to execute SQL statements.
341 */
342 Ext.define('Ext.device.sqlite.SQLTransaction', {
343 id: 0,
344 active: false,
345 statements: null,
346
347 constructor: function(id) {
348 this.id = id;
349 this.statements = new Array();
350 },
351
352 /**
353 * Executes an SQL statement.
354 *
355 * @param {Object} config
356 * The object which contains the following config options:
357 *
358 * @param {String} config.sqlStatement This is required.
359 * The SQL statement to execute.
360 *
361 * @param {Array} config.arguments This is optional.
362 * The arguments array to bind each '?' placeholder in the SQL statement.
363 *
364 * @param {Function} config.callback This is optional.
365 * The callback to be called when the SQL statement succeeded.
366 *
367 * @param {Ext.device.sqlite.SQLTransaction} config.callback.transaction
368 * The transaction of the SQL statement.
369 *
370 * @param {Ext.device.sqlite.SQLTransaction} config.callback.resultSet
371 * The result of the SQL statement.
372 *
373 * @param {Function} config.failure This is optional.
374 * The callback to be called when an error occurred.
375 * If the callback returns false, next SQL statement will be executed.
376 *
377 * @param {Ext.device.sqlite.SQLTransaction} config.failure.transaction
378 * The transaction of the SQL statement.
379 *
380 * @param {Object} config.failure.error
381 * The occurred error.
382 *
383 * @param {Object} config.scope
384 * The scope object
385 */
386 executeSql: function(config) {
387 if (!this.active) {
388 Ext.Logger.error('Ext.device.sqlite.SQLTransaction#executeSql: An attempt was made to use a SQLTransaction that is no longer usable.');
389 return null;
390 }
391
392 if (config.sqlStatement == null) {
393 Ext.Logger.error('Ext.device.sqlite.SQLTransaction#executeSql: You must specify a `sqlStatement` for the transaction.');
394 return null;
395 }
396
397 this.statements.push({
398 sqlStatement: config.sqlStatement,
399 arguments: config.arguments,
400 callback: config.callback,
401 failure: config.failure,
402 scope: config.scope
403 });
404 }
405 }, function() {
406 /**
407 * The SQLResultSet class which is used to represent SQL statements results.
408 */
409 Ext.define('Ext.device.sqlite.SQLResultSet', {
410 insertId: 0,
411 rowsAffected: 0,
412 rows: null,
413
414 constructor: function(data) {
415 this.insertId = data.insertId;
416 this.rowsAffected = data.rowsAffected;
417 this.rows = Ext.create('Ext.device.sqlite.SQLResultSetRowList', data);
418 },
419
420 /**
421 * Returns the row ID of the last row that the SQL statement inserted into the database,
422 * if the statement inserted any rows.
423 * If the statement did not insert a row, throws an exception.
424 *
425 * @return {Number}
426 * The inserted row ID.
427 */
428 getInsertId: function() {
429 if (this.insertId != 0) {
430 return this.insertId;
431 } else {
432 Ext.Logger.error('Ext.device.sqlite.SQLResultSet#getInsertId: An SQLTransaction did not insert a row.');
433 return null;
434 }
435 },
436
437 /**
438 * Returns the number of rows that were changed by the SQL statement.
439 * If the statement did not change any rows, returns zero.
440 *
441 * @return {Number}
442 * The number of rows affected.
443 */
444 getRowsAffected: function() {
445 return this.rowsAffected;
446 },
447
448 /**
449 * Returns a {@link Ext.device.sqlite.SQLResultSetRowList} instance representing rows returned by the SQL statement.
450 *
451 * @return {Ext.device.sqlite.SQLResultSetRowList}
452 * The rows.
453 */
454 getRows: function() {
455 return this.rows;
456 }
457 }, function() {
458 /**
459 * The SQLResultSetRowList class which is used to represent rows returned by SQL statements.
460 */
461 Ext.define('Ext.device.sqlite.SQLResultSetRowList', {
462 names: null,
463 rows: null,
464
465 constructor: function(data) {
466 this.names = data.names;
467 this.rows = data.rows;
468 },
469
470 /**
471 * Returns the number of rows returned by the SQL statement.
472 *
473 * @return {Number}
474 * The number of rows.
475 */
476 getLength: function() {
477 return this.rows.length;
478 },
479
480 /**
481 * Returns a row at specified index returned by the SQL statement.
482 * If there is no such row, returns null.
483 *
484 * @param {Number} index This is required.
485 * The index of a row.
486 *
487 * @return {Object}
488 * The row.
489 */
490 item: function(index) {
491 if (index < this.getLength()) {
492 var item = {};
493 var row = this.rows[index];
494 this.names.forEach(function(name, index) {
495 item[name] = row[index];
496 });
497
498 return item;
499 }
500
501 return null;
502 }
503 });
504 });
505 });
506 });
507 });